Имя: Пароль:
1C
1С v8
Удаление дубликатов из ТаблицыЗначений
,
0 Rounder
 
11.04.13
13:09
Есть таблица значений  ТЗ1 на 150К записей.
Есть в ней колонка "Ключ".
Задача: оставить в таблице только по одной записи на "ключ". Причем если на ключ есть несколько записей - то нужно оставить последнюю.

Реализовал так:
создал ТЗ2 - пустая ТЗ со структурой ТЗ1.

Иду циклом по ТЗ1 с конца к началу.
На каждой итерации с помощью ТЗ2.Найти()
ищу есть ли в ней уже такой ключ, если нету - добавляю запись, если есть - прохожу мимо.

ИсходноеКоличество = ТЗ1.Количество();
   Для Сч = 0 По ИсходноеКоличество - 1 Цикл
       НайденКлюч = ТЗ2.Найти(ТЗ1[ИсходноеКоличество - Сч - 1].Ключ, "Ключ");
       Если НайденКлюч = Неопределено Тогда
           НоваяСтр = ТЗ2.Вставить(0);
           НоваяСтр.Ключ                    = ТЗ1[ИсходноеКоличество - Сч - 1].Ключ;
       КонецЕсли;
   КонецЦикла;

Можно ли оптимальней сделать? Долго отрабатывает.
1 Ursus maritimus
 
11.04.13
13:10
Можно
2 Rounder
 
11.04.13
13:11
Как?
3 acsent
 
11.04.13
13:12
сортировка и обход
4 eklmn
 
гуру
11.04.13
13:13
запросом
5 Rounder
 
11.04.13
13:14
(3) Сортировка ничего не даст - если не вводить дополнительных реквизитов ТЗ, т.к. у записей с одинаковым ключом нет идентификаторов нахождения на временной оси или в таблице значение (номер строки).
6 Rounder
 
11.04.13
13:15
(4) Можно пример?
7 zladenuw
 
11.04.13
13:27
(0) а замером, где долго ?
9 Maxus43
 
11.04.13
13:31
(6) пример чего? ТЗ в запрос как воткнуть?
10 Rounder
 
11.04.13
13:31
(7) Приведенный цикл.
11 Rounder
 
11.04.13
13:33
(9) Пример запроса - как отобрать из массива данных только по одной записи на ключ.
Условие в (0) описано.
12 zladenuw
 
11.04.13
13:33
ВЫБРАТЬ
   ВложенныйЗапрос.Поле1 КАК Ключ,
   ВложенныйЗапрос.Поле2 КАК НомерСтроки
ИЗ
   (ВЫБРАТЬ
       "Ключ" КАК Поле1,
       1 КАК Поле2
   
   ОБЪЕДИНИТЬ ВСЕ
   
   ВЫБРАТЬ
       "Ключ",
       2) КАК ВложенныйЗапрос
       ВНУТРЕННЕЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
           МАКСИМУМ(ВложенныйЗапрос.Поле1) КАК Поле1,
           МАКСИМУМ(ВложенныйЗапрос.Поле2) КАК Поле2
       ИЗ
           (ВЫБРАТЬ
               "Ключ" КАК Поле1,
               1 КАК Поле2
           
           ОБЪЕДИНИТЬ ВСЕ
           
           ВЫБРАТЬ
               "Ключ",
               2) КАК ВложенныйЗапрос) КАК ВложенныйЗапрос1
       ПО ВложенныйЗапрос.Поле1 = ВложенныйЗапрос1.Поле1
           И ВложенныйЗапрос.Поле2 = ВложенныйЗапрос1.Поле2
13 Rounder
 
11.04.13
13:33
И условие дополнено еще в (3)
14 palpetrovich
 
11.04.13
13:33
ТЗ в ВТ + выбрать различные по ключу ...только надо подумать над "нужно оставить последнюю."
15 Rounder
 
11.04.13
13:35
(12) Благодарю. Буду смотреть.
16 Maxus43
 
11.04.13
13:35
>>то нужно оставить последнюю
это только нумерацию строк если сделать.
Чито учитывая размер таблицы даже в запросе будет тормозить
17 acsent
 
11.04.13
13:36
(5) сортировка даст то что одинаковые ключи будут друг за другом идти
18 acsent
 
11.04.13
13:36
Пока Ключ = СтарыйКлюч Цикл
 Удалить()
19 Rounder
 
11.04.13
13:38
(16) Так это и есть главный вопрос - нет поля для доп. упорядочивания.

(17) А сортировка как будет определять какой из ключей выше и ниже поставить если они одинаковы?

Я знаю как решить проблему если добавить реквизит - например НомерСтроки.

Я не знаю как решить ее при условиях описанных в (0) и (3).

Пойду пока изучать запрос из (12).
20 PCcomCat
 
11.04.13
13:38
(0) ТаблицаЗначений откуда берется?
21 SherifSP
 
11.04.13
13:40
А попробуй знаешь как, не записывать уникальный ключ в новую тз, а перебирать строки текущей тз и методом найти строки, искать строку по ключу.
Для Каждого СтрокаТЗ Из ТЗ1 Цикл
   Отбор = Новый Структура;
   Отбор.Вставить("Ключ" СтрокаТЗ.Ключ);
   ИскомаяСтрока= ТЗ1.НайтиСтроки(Отбор);
   Если ИскомаяСтрока.Количество() > 1 Тогда
       //Удаляешь строку ТЗ у которой в колонке "НомерСтроки" меньше значение
   КонецЕсли;
КонецЦикла;
22 Rounder
 
11.04.13
13:40
(20) Не важно откуда она берется.
Вопрос ведь в ином.
Если я добавлю поле доп упорядочивания - то я решу свою задачу.
Вопрос как решить задачу без доп поля.
23 Maxus43
 
11.04.13
13:41
(22) дак (12) это не решает
24 Rounder
 
11.04.13
13:42
(21) Нету НомераСтроки :)
25 Rounder
 
11.04.13
13:42
(23) Похоже на то.
26 palpetrovich
 
11.04.13
13:42
(24) да?!! в ТЗ нет НомерСтроки?
27 zladenuw
 
11.04.13
13:43
(24) а там она и не надо

Если ИскомаяСтрока.Количество() > 1 Тогда
       Индекс = 0;
       Пока Истина Цикл
          Если ИскомаяСтрока.Количество()=1 Тогда
             Прервать;
          Иначе
             ИскомаяСтрока.Удалить(Индекс);
             Индекс=Индекс+1;    
          КонецЕсли;
       КонецЦикла;
   КонецЕсли;
28 Maxus43
 
11.04.13
13:43
(24) номера нет, есть ИНдекс
29 Rounder
 
11.04.13
13:44
(26) Я не это имел в виду - нет реквизита НомерСтроки - который не изменится после сортировки.
30 PCcomCat
 
11.04.13
13:44
(22) Если эта ТЗ из запроса получилась, то разница есть: нафига второй раз анализировать, если можно в первом сразу получить нужный результат?
31 Rounder
 
11.04.13
13:46
(30) Еще раз говорю - задача не в этом.
Есть условия описанные в (0) и в (3). Нужно ответить на один вопрос: возможна ли более оптимальная реализация нежели приведенная в (0).
32 Rounder
 
11.04.13
13:48
(27) Не прокатит. Точнее прокатит - если все одинаковые ключи будут идти неделимыми группами.

Или я не прав?
33 zladenuw
 
11.04.13
13:49
(32) отбор по таблице идет. ты получаешь массив строк где ключ = ключотбора
34 Gossar1C
 
11.04.13
13:50
еще можно свернуть)
35 Gossar1C
 
11.04.13
13:51
ТаблицаЗначений (ValueTable)
Свернуть (GroupBy)
Синтаксис:

Свернуть(<КолонкиГруппировок>, <КолонкиСуммирования>)
Параметры:

<КолонкиГруппировок> (обязательный)

Тип: Строка.
Имена колонок, разделенные запятыми, по которым необходимо группировать строки таблицы значений.
<КолонкиСуммирования> (необязательный)

Тип: Строка.
Имена колонок, разделенные запятыми, по которым необходимо суммировать значения в строках таблицы значений.
Описание:

Осуществляет свертку таблицы значений по указанным колонкам группировки. Строки, у которых совпадают значения в колонках, указанных в первом параметре, сворачиваются в одну строку. Значения этих строк, хранящиеся в колонках, указанных во втором параметре, накапливаются.
Важно! Списки колонок не должны пересекаться. Колонки, не вошедшие ни в один из списков колонок, после выполнения метода удаляются из таблицы значений.

Доступность:

Сервер, толстый клиент, внешнее соединение.
36 Rounder
 
11.04.13
13:53
(35) Это ничего не дает.

(33) Да я суть понял. Сомнителен выигрыш по времени выполнения.
37 zladenuw
 
11.04.13
13:53
(36) запрос. где группировки по максимуму. можно поидее без вложенных. только нужен индекс строки ключа
38 Rounder
 
11.04.13
13:54
Ладно.
Всем спасибо.
Буду наверное вводить еще одно поле для доп. упорядочивания.
39 Gossar1C
 
11.04.13
13:55
(36) делал загрузку с 2х прайсов притом в одном прайсе была одна строка с брендом Dextrim допустим, а во втором их 3 и более с разными номерами замены, мне этот метод очень помог
40 Rounder
 
11.04.13
13:57
(39) В данном случае он не подходит. Кроме поля "Ключ" в ТЗ есть еще несколько полей.
В (0) приводил код только для "Ключ" для простоты восприятия.
41 PiterPrg
 
11.04.13
14:03
(40) тогда что-то типа так:

   ТЗКол = ТЗ1.Скопировать( , "Ключ");
   ТЗКол.Индексы.Добавить("Ключ");
   ТЗКол.Свернуть("Ключ", "");
   
   Сч = ТЗ1.Количество() - 1;
   Пока Сч >= 0 Цикл
       Стр = ТЗ1[Сч];
       СтрКол = ТЗКол.Найти(Стр.Ключ, "Ключ");
       Если СтрКол = Неопределено Тогда
           ТЗ1.Удалить(Стр);
       Иначе
           ТЗКол.Удалить(СтрКол);
       КонецЕсли;
       Сч = Сч - 1;
   КонецЦикла
42 sirsp
 
11.04.13
14:33
ТЗ2 = ТЗ1.СкопироватьКолонки();

лС = новый Соответствие;
Для н = - ТЗ1.Количество() + 1 По 0 Цикл
   лСтрока = ТЗ1[-н];
   Если лС[лСтрока.Ключ] = неопределено Тогда
       лС.Вставить(лСтрока.Ключ, ИСТИНА);
       ЗаполнитьЗначенияСвойств(ТЗ2.Вставить(0), лСтрока);
   КонецЕсли;
КонецЦикла;
43 Рэйв
 
11.04.13
14:50
(0)  как то так:

мСтрок=ТЗ.НайтиСтроки(Новый Структура("КолонкаКлюч",ЗначениеКлюч));
Если мСтрок.Количество()>1 Тогда
  Для Каждого строчкаТЗ Из мСтрок Цикл
     Если мСтрок.Индекс(ЭстрочкаТЗ)+1=мСтрок.Количество() Тогда  
          Прервать;
     КонецЕсли;
     ТЗ.Удалить(строчкаТЗ);
  Конеццикла;
КонецЕсли;
44 palpetrovich
 
11.04.13
14:58
нарисовал на примере справочника НоменклатураЮ может пригодится?

   // подготовка ТЗ
   Запрос = Новый Запрос;
   Запрос.Текст = "ВЫБРАТЬ
   |    Номенклатура.Код,
   |    Номенклатура.Наименование
   |ИЗ
   |    Справочник.Номенклатура КАК Номенклатура
   |";
   Результат = Запрос.Выполнить();
   
   // собствеено (выводим тольлко различные по наименованию)
   Запрос = Новый Запрос;
   МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
   Запрос.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
   Запрос.Текст = "ВЫБРАТЬ
   |    ТЗ.Код,
   |    ВЫРАЗИТЬ(ТЗ.Наименование КАК СТРОКА(100)) КАК Наименование
   |ПОМЕСТИТЬ ВТ
   |ИЗ &ТЗ КАК ТЗ
   |;
   |ВЫБРАТЬ РАЗЛИЧНЫЕ
   |    ВТ.Наименование КАК Наименование
   |ПОМЕСТИТЬ ВТ1
   |ИЗ ВТ КАК ВТ
   |;
   |ВЫБРАТЬ
   |    ВТ1.Наименование КАК Наименование,
   |    ВТ.Код
   |ИЗ
   |    ВТ1 КАК ВТ1
   |        ЛЕВОЕ СОЕДИНЕНИЕ
   |            (ВЫБРАТЬ
   |            ВТ.Наименование КАК Наименование,
   |            МИНИМУМ(ВТ.Код) КАК Код
   |            ИЗ ВТ КАК ВТ
   |            СГРУППИРОВАТЬ ПО
   |            ВТ.Наименование) КАК ВТ
   |        ПО (ВТ.Наименование = ВТ1.Наименование)
   |УПОРЯДОЧИТЬ ПО
   |    Наименование";
   Запрос.УстановитьПараметр("ТЗ", ТЗ);
   Результат = Запрос.Выполнить();
45 palpetrovich
 
11.04.13
15:05
* после 1-го запроса забыл    ТЗ = Результат.Выгрузить();