Как найти дубли в таблице значений 1с

Поиск дублей в таблице значений или табличном поле

16.05.2017

Проверка любой таблицы на повторяющиеся строки, с выводом сообщения какие строки задублированны, и режимом “Отказ” для проведения документов.

В данной процедуре поиск дублей происходит по всем колонкам строк. Тоесть дублем считается повтор значений в двух строках по всем колонкам.

Если повторяющих строк больше одной, то они все будут показаны в сообщении, с порядковым номером исходной строки и повторяющейся строки.

Данную процедура лучше использовать в общем модуле и вызывать её из процедур “ПриЗаписи” или “ОбработкаПроведения”, и т.п. То есть в тех, где можно сделать отказ от выполнения дальнейших дествий
В качестве Таблицы — можно указать “ТаблицуЗначений”, либо “ТабличнуюЧасть” документа, Справочника и т.д.

// !!! Павел С.С. 2 июня 2011 г. 17:49:14
// Проверка любой таблицы на повторяющиеся строки
// Данную процедура лучше использовать в процедурах ПриЗаписи, ОбработкаПроведения, и т.п. Тоесть в тех, где можно сделать отказ от выполениядальнейших дествий
// Параметры:
// ТЧ — можно указать «ТаблицуЗначений», либо «ТабличнуюЧасть» документа
// Отказ — параметр «Отказ» или «Истина», «Ложь»
Процедура ПоискДублейСтрокВТабЧасти(ТЧ, Отказ) Экспорт

 Если Тип(ТЧ) = Тип(«ТаблицаЗначений») Тогда
 
ТаблЗнач = ТЧ;
 Иначе
 
ТаблЗнач = ТЧ.Выгрузить();
 КонецЕсли;

 Отбор = Новый Структура();

 Для каждого Стр Из ТаблЗнач Цикл
 
Отбор.Очистить();
  Для каждого
Колонки Из ТаблЗнач.Колонки Цикл
   Если
Колонки.Имя <> «НомерСтроки» Тогда
   
Отбор.Вставить(Колонки.Имя, Стр[Колонки.Имя]);
   КонецЕсли;
  КонецЦикла;

  Строки = ТаблЗнач.НайтиСтроки(Отбор);
  Если
Строки.Количество() > 1 Тогда
   Для каждого
НайденныеСтроки Из Строки Цикл
    Если
Строки.Найти(НайденныеСтроки) > 0 Тогда
    
СтрокаСообщения = «Строка № » + Строки[0].НомерСтроки + » совпадает со строкой № » + НайденныеСтроки.НомерСтроки;
    
СообщитьОбОшибке(СтрокаСообщения, Отказ, «Уберите задвоения строк!»);
    КонецЕсли;
   КонецЦикла;
     КонецЕсли;
 КонецЦикла;
КонецПроцедуры

Поиск “дублей” в таблице значений

Я
   ПравильнаЯ

14.03.06 – 17:41

Как в таблице значений найти записи с одинаковыми колонками? Есть таблица значений из 11 колонок, из них две РасчетныйСчет и КодБанка. Надо оставить строки с повторяющимися значениями колонок РасчетныйСчет и КодБанка, те оставить строки с неуникальными значениями этих колонок. Подскажите какие нибудь идеи к алгоритму.

   Токс

1 – 14.03.06 – 17:43

Самая простая идея: Свернуть()…

   smaharbA

2 – 14.03.06 – 17:45

+ (1) На всякий случай Свернуть, Свернуть, Свернуть…

   Мулька

3 – 14.03.06 – 17:46

Перед КонецЦикла запоминаешь значение.

После начала цикла сравниваешь новое значение с запомненным. Если равно – дубль

   smaharbA

4 – 14.03.06 – 17:46

(3) Афигеть…

   Мулька

5 – 14.03.06 – 17:46

(3) + ест-но перед циклом сортировка по искомому полю

   Omega

6 – 14.03.06 – 17:47

(3) по двум искомым полям? )

   smaharbA

7 – 14.03.06 – 17:47

+ (4) А первое значение с чем сравнивать с “пустышкой” ?

   Мулька

8 – 14.03.06 – 17:47

(4) Афигевай

   Allexe

9 – 14.03.06 – 17:47

(0) Может проще на этапе формирования?

Для к=1 по ТЗ.КоличествоСтрок Цикл

ТЗ.ПолучитьСтроку(к);

Если ТЗ.РасчетныйСчет<>ТЗ.КодБанка Тогда

ТЗ.УдалитьСтроку(к)

КонецЕсли;

КонецЦикла;

   smaharbA

10 – 14.03.06 – 17:48

(9) Ты этт счас придумал

(8) А Свернуть нафиха тогда есть?

   Каанкереде

11 – 14.03.06 – 17:48

уж сотка ближется, а автора все нет…

   Мулька

12 – 14.03.06 – 17:50

(6) – по двум полям – два прогона ТЗ.

(10) И что ты узнаешь в результате сворачивания ? Разве что в доп.поле единицу прописать перед сворачиванием в каждую строку. Потом проверять на Если >=2

   КонецЦикла

13 – 14.03.06 – 17:50

Я подожду (цы)

   По идее так 2

14 – 14.03.06 – 17:52

Метод удаления строк из конкурса с проклаба (не помню кто автор):

       Перем ТЗ_СЛУЖ;

       тз = СоздатьОбъект(“ТаблицаЗначений”);

       //тз – исходная табличка значений

       //Заполнение тз:

        ………………

       // Удалим не дубли

     КонСтрока=Мин(тз.НоваяКолонка(“СЛУЖ_КОЛ”), 0);

   тз.ВыбратьСтроки();

   Пока тз.ПолучитьСтроку()=1 Цикл

      Если ТЗ.РасчетныйСчет=ТЗ.КодБанка     Тогда

       КонСтрока=КонСтрока+1;

       тз.СЛУЖ_КОЛ=1;

      КонецЕсли;

   КонецЦикла;                      

   Если (КонСтрока=0) ИЛИ (КонСтрока=тз.КоличествоСтрок()) Тогда

       тз.КоличествоСтрок(КонСтрока);

       тз.УдалитьКолонку(“СЛУЖ_КОЛ”);

   Иначе

       тз.Сортировать(“-СЛУЖ_КОЛ”);

       тз.УдалитьКолонку(“СЛУЖ_КОЛ”);

       тз.Выгрузить(ТЗ_СЛУЖ,,КонСтрока);

       тз=ТЗ_СЛУЖ;

   КонецЕсли;

   ПравильнаЯ

15 – 14.03.06 – 17:52

(11) Тута я. Читаю и думаю что применять…

   Allexe

16 – 14.03.06 – 17:53

(10) Ваш код в студию

ТЗ.Свернуть(1,2,3,4,5,6,7,8,9,10,11) Вы так предлагаете?

   smaharbA

17 – 14.03.06 – 17:53

(12)

Вы не знаете кто такой Паниковский?

Нет, Вы не знаете кто такой Паниковский…

Тогда поезжайте в Киев и спросите.

Спросите…

Нет, Вы поезжайте и спросите… (с)

   Мулька

18 – 14.03.06 – 17:53

(15) Ну а если что б оставить только уникальные – то см.(9) тока, как говорится сзаду надо это делать. Ну и два прогона – по любому.

   smaharbA

19 – 14.03.06 – 17:54

(16) Не пристало мне код тута писать на такой “тривиал” 🙂

   romix

Модератор

20 – 14.03.06 – 17:54

тз=СоздатьОбъект("ТаблицаЗначений");
тз.НоваяКолонка("РассчетныйСчет");
тз.НоваяКолонка("КодБанка");
тз.НоваяКолонка("Количество");


 //Заполняем единицами Количество
 

тз.Заполнить(1,,,"Количество"); 

...

тз.Свернуть("РассчетныйСчет,КодБанка","Количество");

Теперь в графе количество будет 2 и более если задвоены строки.

   smaharbA

22 – 14.03.06 – 17:55

(18) По любому ?

   Мулька

23 – 14.03.06 – 17:55

(22) По любому – для простоты. Из принципа – ест-но и гланды можно снизу выдрать

   Allexe

24 – 14.03.06 – 17:56

🙂

Пусть скажет аффтар в (0)

   Таня

25 – 14.03.06 – 17:57

согласна с (19)

   Allexe

26 – 14.03.06 – 17:58

(25) В чем согласна?

   ПравильнаЯ

27 – 14.03.06 – 17:59

(24) Вот (20) точно можно использовать, в (14) еще не врубилась

   КонецЦикла

28 – 14.03.06 – 18:00

(27) Да, только не забудь, что нужно узнать не сам факт существования строк, а еще ведь и удалить их 🙂

   Таня

29 – 14.03.06 – 18:01

(26) в том что “Не пристало мне код тута писать на такой “тривиал”

   ПравильнаЯ

30 – 14.03.06 – 18:03

(27) так все в другую таблицу и там удалить строки у которых Количество>1

   ПравильнаЯ

31 – 14.03.06 – 18:08

(28) Смотри (30)

   КонецЦикла

32 – 14.03.06 – 18:11

(30) Их там еще нужно найти… а однозначно это сделать не получится

Короче перебирай строки таблицы и попутно найденные значения заноси в список, например

Если нет в списке – добавить, если есть – удалить тек. строку

Проще, конечно, при формировании ТЗ это сделать

Долго, но верно

ЗЫ. имхо

   romix

Модератор

33 – 14.03.06 – 18:15

(28) Два вложенных цикла. По свернутой таблице и по искомой.

Не забывать делать ВыбратьСтроки(); после каждого удаления строки.

Тупой надежный алгоритм. Но при больших таблицах может выполняться долго.

   ПравильнаЯ

34 – 14.03.06 – 18:18

(32) Однозначно получиться – пронумеровать строки то я могу (Добавить еще одну колонку в ТаблицуЗначений). Но мой метод через две таблицы

(33) Вот так и попробую сделать. спасибо

   Токс

35 – 14.03.06 – 18:18

Уж тогда уж Сортировать()… и далее…

   smaharbA

36 – 14.03.06 – 18:22

Всеж непонял, а чем Свернуть неглянется ?

Поясните для тупых и алкоголиков…(правдо)

Млж чего и посоветую, и вопряки (19) код койнибудь напишу…

   КонецЦикла

37 – 14.03.06 – 18:22

(33) Мдя… зачем два, когда можно ОДНИМ?

(34) Угумс… пронумеровать и благополучно свернуть 🙂

   Shaman100M

38 – 14.03.06 – 18:40

(0) А в колонках значения повторяются?

   Тык

39 – 14.03.06 – 18:50

Мулька в (3)+(5) самую правильную наводку дал …

А всё остальное, что здесь сказали – чушь собачья!

   Таня

40 – 14.03.06 – 18:53

(39) эт точно 🙂

   Shaman100M

41 – 14.03.06 – 18:56

+(3) + (5) + (39) Свертка ни при чем.

Можно такой алгоритм.

1. Таб2 с колонками “ОбщийСчет”,”Ссылка”

2. Данные из ТЗ в Таб2 построчно:

    Таб2.ОбщийСчет=ТЗ.РасчетныйСчет;

    Таб2.Ссылка=ТЗ.НомерСтроки;

3. Данные из ТЗ в Таб2 повторно:

    Таб2.ОбщийСчет=ТЗ.КодБанка;

    Таб2.Ссылка=ТЗ.НомерСтроки;

4. Сортировка

    Таб2.Сортировать(“ОбщийСчет”);

5. Удаление строк из Таб2 для которых “ОбщийСчет” не повторяется.

6. Сортировка Таб2 по “Ссылка” – итог: номера строк в ТЗ, которые надо оставить.

   smaharbA

42 – 14.03.06 – 19:01

(39) А это полная лажо ?

Процедура Сформировать()

   ТЗ=СоздатьОбъект(“ТаблицаЗначений”);

   ТЗ.НоваяКолонка(“КодБанка”);

   ТЗ.НоваяКолонка(“РасчетныйСчет”);

   ТЗ.НоваяКолонка(“названиеБанка”);

   ТЗ.НоваяКолонка(“Остаток”);

   ТЗ.НоваяСтрока();

   ТЗ.КодБанка = 1;

   ТЗ.РасчетныйСчет = 2;

   ТЗ.названиеБанка = “Пупкинский”;

   ТЗ.Остаток = 256;

   ТЗ.НоваяСтрока();

   ТЗ.КодБанка = 1;

   ТЗ.РасчетныйСчет = 2;

   ТЗ.названиеБанка = “ЗаПупкинский”;

   ТЗ.Остаток = 256;

   ТЗ.НоваяСтрока();

   ТЗ.КодБанка = 2;

   ТЗ.РасчетныйСчет = 2;

   ТЗ.названиеБанка = “ОччПупкинский”;

   ТЗ.Остаток = 253;

   ТЗ.Свернуть(“КодБанка,РасчетныйСчет”,”НазваниеБанка,Остаток”);

   ТЗ.ВыбратьСтроку();

КонецПроцедуры

   Токс

43 – 14.03.06 – 19:09

(42) Не буянь, лажевинько…

   smaharbA

44 – 14.03.06 – 19:12

(43) Но ведь (0) решает, кто не верит (не о тебе) может проверить…(дублито сказано тока по 2 колонкам зырить, знчит значения остальных пох какое выйдет, хотя могу кинуть чтоб и все значения было видно, при чем будет “приляпано” к тем двум калонкам)

   smaharbA

45 – 14.03.06 – 19:13

+(44) Если есть колонки с числовым значением но их ссумировать ненада, то легко преобразуются до текстовых перед тем как…

   Токс

46 – 14.03.06 – 19:16

(44)Так вот и надо, получается, эти дубляные строки совсем не трогать… Я сам (0) по диагонали прочитал… Криво здесь Свёртывать()…

Из принципа можно и её прикрутить, но это будет “пятая нога”…

   Токс

47 – 14.03.06 – 19:18

(45) Так к чему не преобразовывай, а данные из других колонок потеряешь…

   smaharbA

48 – 14.03.06 – 19:19

(46)(47) Тогда решения для (0) нету кроме (44) в скобочках после второй запятой…

   Shaman100M

49 – 14.03.06 – 19:20

(44) не решает.

   smaharbA

50 – 14.03.06 – 19:20

+(48) “…после первой запятой”

   smaharbA

51 – 14.03.06 – 19:22

(49) Почитай (0) сказано по двум колонкам? – сказано, тогда остальные как уж получатся, суммы будут верны, а строки/субконтывсякие будут по первой, хотя в (44) в скобочках есть вариант чтоб и все другие колонки остались целы….

   smaharbA

52 – 14.03.06 – 19:24

+(51) а вот как сделать чтоб было по 2 колонкам, но остальные видны – думайте, я “в танке”, нескажу…

   Shaman100M

53 – 14.03.06 – 19:24

(51) смотря что ты будешь делать после (42) Но в любом случае, свертка по 2 полям лишняя.

   smaharbA

54 – 14.03.06 – 19:25

(53) Ты пробовал ? все что в (0) описано сделано, остальное в (44) и (52)…

   Shaman100M

55 – 14.03.06 – 19:26

КодБанка РасчетныйСчет

2        4

4        2

После свертки останется в первозданном виде.

   smaharbA

56 – 14.03.06 – 19:29

(55) Да ну на… ты в точности повторил ?

КодБанка РасчетныйСчет НазваниеБанка        Остаток

1        2                Пупкинский        512

2        2                ОччПупкинский2    253

   smaharbA

57 – 14.03.06 – 19:31

Или такая лажо

Процедура Сформировать()

   ТЗ=СоздатьОбъект(“ТаблицаЗначений”);

   ТЗ.НоваяКолонка(“КодБанка”);

   ТЗ.НоваяКолонка(“РасчетныйСчет”);

   ТЗ.НоваяКолонка(“названиеБанка”);

   ТЗ.НоваяКолонка(“Остаток”);

   ТЗ.НоваяСтрока();

   ТЗ.КодБанка = 1;

   ТЗ.РасчетныйСчет = 2;

   ТЗ.названиеБанка = “Пупкинский”;

   ТЗ.Остаток = 256;

   ТЗ.НоваяСтрока();

   ТЗ.КодБанка = 2;

   ТЗ.РасчетныйСчет = 2;

   ТЗ.названиеБанка = “ОччПупкинский”;

   ТЗ.Остаток = 253;

   ТЗ.НоваяСтрока();

   ТЗ.КодБанка = 1;

   ТЗ.РасчетныйСчет = 2;

   ТЗ.названиеБанка = “ЗаПупкинский”;

   ТЗ.Остаток = 256;

   ТЗ.НоваяСтрока();

   ТЗ.КодБанка = 2;

   ТЗ.РасчетныйСчет = 2;

   ТЗ.названиеБанка = “ччПупкинский”;

   ТЗ.Остаток = 253;

   ТЗ.Свернуть(“КодБанка,РасчетныйСчет”,”НазваниеБанка,Остаток”);

   ТЗ.ВыбратьСтроку();

КонецПроцедуры

   Shaman100M

58 – 14.03.06 – 19:34

(57) крылья крылья… Хвост! на.. тебе свертка? Сортировка + прогон для повторов которые идут подряд! Сначала для “КодБанка” потом для “Расчетного счета”. Как и говорил Мулька.

  

smaharbA

59 – 14.03.06 – 19:38

(58) А на сортировать и прогонять если и так все решается, если конечно условия сложнее тогда мож и сортировать…

Проверка на наличие дублирующихся строк в табличных частях

Время прочтения – 7 мин.

Получить бесплатную консультацию

Думаю, каждый программист рано или поздно сталкивался с подобной задачей: как реализовать проверку на наличие дублирующихся строк в табличных частях. Кому-то просто нужно проверить: есть, или нет, дубли. Кому-то нужно известить пользователя, и сообщить ему номера строк с дублями. Это вопросы будут рассмотрены в данной статье. Но давайте сразу определимся с терминологией: поля, по которым будет осуществляться контроль, назовем «ключевые».


Итак, предположим, что у нас стоит задача проверить наличие дублей строк табличной части по ключевым полям. Пусть это будет документ «Реализация товаров, услуг», дубли строк мы будем искать в табличной части «Товары», а в качестве ключевых полей будем использовать следующие реквизиты табличной части: Качество, Номенклатура, Склад, СерияНоменклатуры и ХарактеристикаНоменклатуры.



Рассмотрим следующие ситуации:


1) Проверка на наличие дублирующихся строк на уровне есть/нет.
Реализация ее будет выглядеть следующим образом:

Запрос = Новый Запрос;
Запрос.УстановитьПараметр(“Ссылка”, Ссылка);
Запрос.Текст =
“ВЫБРАТЬ
| ТабЧасть.Качество,
| ТабЧасть.Номенклатура,
| ТабЧасть.СерияНоменклатуры,
| ТабЧасть.Склад,
| ТабЧасть.ХарактеристикаНоменклатуры,
| КОЛИЧЕСТВО(ТабЧасть.НомерСтроки) КАК КоличествоДублей
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК ТабЧасть
|ГДЕ
| ТабЧасть.Ссылка = &Ссылка
|
|СГРУППИРОВАТЬ ПО
| ТабЧасть.Качество,
| ТабЧасть.Номенклатура,
| ТабЧасть.СерияНоменклатуры,
| ТабЧасть.Склад,
| ТабЧасть.ХарактеристикаНоменклатуры
|
|ИМЕЮЩИЕ
| КОЛИЧЕСТВО(ТабЧасть.НомерСтроки) > 1″;
РезЗапроса = Запрос.Выполнить();
Если Не РезЗапроса.Пустой() Тогда
              Сообщить(“Имеются дубли строк!!!”); 
КонецЕсли;

Здесь все достаточно просто. Обращаемся к данным табличной части документа, группируем по ключевым полям с использование агрегатной функции для подсчета количества номеров строк. Накладываем условие по вычисленному значению агрегатной функции. Все.

2) Классика жанра. Выведем в сообщении пользователю номера всех тех строк табличной части, которые являются дублями.

Запрос = Новый Запрос; 

Запрос.УстановитьПараметр(“Ссылка”, Ссылка); 

Запрос.Текст =
“ВЫБРАТЬ 
| ТабЧасть.Качество, 
| ТабЧасть.Номенклатура, 
| ТабЧасть.СерияНоменклатуры, 
| ТабЧасть.Склад, 
| ТабЧасть.ХарактеристикаНоменклатуры, 
| ТабЧасть.НомерСтроки КАК НомерСтроки 
|ПОМЕСТИТЬ ВТ_ТабЧасть 
|ИЗ 
| Документ.РеализацияТоваровУслуг.Товары КАК ТабЧасть 
|ГДЕ 
| ТабЧасть.Ссылка = &Ссылка 
|; 
| |//////////////////////////////////////////////////////////////////////////////// 
|ВЫБРАТЬ 
| ТабЧасть.Качество КАК Качество, 
| ТабЧасть.Номенклатура КАК Номенклатура, 
| ТабЧасть.СерияНоменклатуры КАК СерияНоменклатуры, 
| ТабЧасть.Склад КАК Склад, 
| ТабЧасть.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры, 
| ТабЧасть.НомерСтроки КАК НомерСтроки 
|ИЗ 
| ВТ_ТабЧасть КАК ТабЧасть 
|ГДЕ 
| (ТабЧасть.Качество, ТабЧасть.Номенклатура, ТабЧасть.СерияНоменклатуры, ТабЧасть.Склад, ТабЧасть.ХарактеристикаНоменклатуры) В 
| (ВЫБРАТЬ 
| ВТ.Качество, 
| ВТ.Номенклатура, 
| ВТ.СерияНоменклатуры, 
| ВТ.Склад, 
| ВТ.ХарактеристикаНоменклатуры 
| ИЗ 
| ВТ_ТабЧасть КАК ВТ 
| СГРУППИРОВАТЬ ПО 
| ВТ.Качество, 
| ВТ.Номенклатура, 
| ВТ.СерияНоменклатуры, 
| ВТ.Склад, 
| ВТ.ХарактеристикаНоменклатуры 
| ИМЕЮЩИЕ 
| КОЛИЧЕСТВО(ВТ.НомерСтроки) > 1) 

|УПОРЯДОЧИТЬ ПО 
| НомерСтроки 
|ИТОГИ ПО 
| Качество, 
| Номенклатура, 
| СерияНоменклатуры, 
| Склад”;
РезЗапроса = Запрос.Выполнить(); 
Если Не РезЗапроса.Пустой() Тогда 
Выб_Качество = РезЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам); 
Пока Выб_Качество.Следующий() Цикл 
Выб_Номенклатура = Выб_Качество.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам); 
Пока Выб_Номенклатура.Следующий() Цикл 
Выб_СерияНоменклатуры = Выб_Номенклатура.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам); 
Пока Выб_СерияНоменклатуры.Следующий() Цикл 
Выб_Склад = Выб_СерияНоменклатуры.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам); 
Пока Выб_Склад.Следующий() Цикл 
Выборка = Выб_Склад.Выбрать(); 
ТекстСообщения = “”; 
Пока Выборка.Следующий() Цикл 
ТекстСообщения = ТекстСообщения + ?(ПустаяСтрока(ТекстСообщения), “”, “, “) + Выборка.НомерСтроки; 
КонецЦикла; 
ТекстСообщения = “Обнаружено дублирование строк: ” + ТекстСообщения; Сообщить(ТекстСообщения); 
КонецЦикла; 
КонецЦикла; 
КонецЦикла; 
КонецЦикла; 

КонецЕсли

Здесь все тоже не слишком сложно. Обратились к данным табличной части документа, поместили их во временную таблицу. Далее работаем со временной таблицей. Обращаемся к ней, отбираем те данные, по которым есть дубли (условие с использованием вложенного запроса). А далее, для того, чтобы сохранить все множество значений номеров строк, нам необходимо по ключевым полям объявить итоги. В таком случае в результате запроса, при использовании механизма итогов, появляются дополнительные строки, в которых хранится итог для того, или иного поля, а сам результат принимает иерархический вид, или вид дерева. Для того, чтобы вывести пользователю сообщение, нам нужно обойти результат запроса, и по каждому набору ключевых полей скомпоновать текст сообщения, и выдать его пользователю.


А теперь попробуем оценить перспективу доработки. Допустим, у нас изменился состав ключевых полей в сторону увеличения их количества: добавились ЕдиницаИзмерения и ЗаказПокупателя. Чтобы контроль дублей строк не перестал работать, нам нужно доработать запрос и обход результата запроса следующим образом:


Запрос = Новый Запрос; 
Запрос.УстановитьПараметр(“Ссылка”, Ссылка); 
Запрос.Текст = 
ВЫБРАТЬ
| ТабЧасть.ЕдиницаИзмерения, 
| ТабЧасть.ЗаказПокупателя, 
| ТабЧасть.Качество, 
| ТабЧасть.Номенклатура, 
| ТабЧасть.СерияНоменклатуры, 
| ТабЧасть.Склад, 
| ТабЧасть.ХарактеристикаНоменклатуры, 
| ТабЧасть.НомерСтроки КАК НомерСтроки 
|ПОМЕСТИТЬ ВТ_ТабЧасть 
|ИЗ 
| Документ.РеализацияТоваровУслуг.Товары КАК ТабЧасть 
|ГДЕ 
| ТабЧасть.Ссылка = &Ссылка 
|; 

|//////////////////////////////////////////////////////////////////////////////// 
|ВЫБРАТЬ 
| ТабЧасть.ЕдиницаИзмерения КАК ЕдиницаИзмерения, 
| ТабЧасть.ЗаказПокупателя КАК ЗаказПокупателя, 
| ТабЧасть.Качество КАК Качество, 
| ТабЧасть.Номенклатура КАК Номенклатура, 
| ТабЧасть.СерияНоменклатуры КАК СерияНоменклатуры, 
| ТабЧасть.Склад КАК Склад, 
| ТабЧасть.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры, 
| ТабЧасть.НомерСтроки КАК НомерСтроки 
|ИЗ 
| ВТ_ТабЧасть КАК ТабЧасть 
|ГДЕ 
| (ТабЧасть.ЕдиницаИзмерения, ТабЧасть.ЗаказПокупателя, ТабЧасть.Качество, ТабЧасть.Номенклатура, ТабЧасть.СерияНоменклатуры, ТабЧасть.Склад, ТабЧасть.ХарактеристикаНоменклатуры) В 
| (ВЫБРАТЬ 
| ВТ.ЕдиницаИзмерения, 
| ВТ.ЗаказПокупателя, 
| ВТ.Качество, 
| ВТ.Номенклатура, 
| ВТ.СерияНоменклатуры, 
| ВТ.Склад, 
| ВТ.ХарактеристикаНоменклатуры 
| ИЗ 
| ВТ_ТабЧасть КАК ВТ 
| СГРУППИРОВАТЬ ПО 
| ВТ.ЕдиницаИзмерения, 
| ВТ.ЗаказПокупателя, 
| ВТ.Качество, 
| ВТ.Номенклатура, 
| ВТ.СерияНоменклатуры, 
| ВТ.Склад, 
| ВТ.ХарактеристикаНоменклатуры 
| ИМЕЮЩИЕ 
| КОЛИЧЕСТВО(ВТ.НомерСтроки) > 1) 

|УПОРЯДОЧИТЬ ПО 
| НомерСтроки 
|ИТОГИ ПО 
| Качество, 
| Номенклатура, 
| СерияНоменклатуры,
| Склад, 
| ХарактеристикаНоменклатуры, 
| ЕдиницаИзмерения”;
РезЗапроса = Запрос.Выполнить(); 
Если Не РезЗапроса.Пустой() Тогда 
Выб_Качество = РезЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам); 
Пока Выб_Качество.Следующий() Цикл 
Выб_Номенклатура = Выб_Качество.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам); 
Пока Выб_Номенклатура.Следующий() Цикл 
Выб_СерияНоменклатуры = Выб_Номенклатура.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам); 
Пока Выб_СерияНоменклатуры.Следующий() Цикл 
Выб_Склад = Выб_СерияНоменклатуры.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам); 
Пока Выб_Склад.Следующий() Цикл 
Выб_ХарактеристикаНоменклатуры = Выб_Склад.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам); 
Пока Выб_ХарактеристикаНоменклатуры.Следующий() Цикл 
Выб_ЕдиницаИзмерения=Выб_ХарактеристикаНоменклатуры.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам); 
Пока Выб_ЕдиницаИзмерения.Следующий() Цикл 
ТекстСообщения = “”; 
Выборка = Выб_ЕдиницаИзмерения.Выбрать(); 
Пока

Подпишитесь на дайджест!

Подпишитесь на дайджест, и получайте ежемесячно подборку полезных статей.

3) Альтернативный вариант. 

Реализация предыдущего варианта другим способом. Предлагаю использовать особенность запросов 1С, позволяющих обращаться к данным табличных частей, как к обычным полям выборки. Ну все-таки не совсем обычным, но все же полям выборки. Кроме того, нам придётся использовать не самый оптимальный способ соединения данных – декартово произведение. Почему так – опишу ниже.

ШаблонОшибки = “Табличная часть ‘Товары’: по реквизитам ‘Качество, Номенклатура, СерияНоменклатуры, Склад, ХарактеристикаНоменклатуры’ обнаружено дублирование строк ‘%1′”; 
Запрос = Новый Запрос; 
Запрос.УстановитьПараметр(“Ссылка”, Объект.Ссылка); 
Запрос.Текст
ВЫБРАТЬ
| ТабЧасть.Ссылка, 
| МИНИМУМ(ТабЧасть.НомерСтроки) КАК МинНомерСтроки, 
| ТабЧасть.Качество, 
| ТабЧасть.Номенклатура, 
| ТабЧасть.СерияНоменклатуры, 
| ТабЧасть.Склад, 
| ТабЧасть.ХарактеристикаНоменклатуры 
|ПОМЕСТИТЬ ВТ_ТабЧасть 
|ИЗ 
| Документ.РеализацияТоваровУслуг.Товары КАК ТабЧасть 
|ГДЕ 
| ТабЧасть.Ссылка = &Ссылка 

|СГРУППИРОВАТЬ ПО 
| ТабЧасть.Ссылка, 
| ТабЧасть.Качество, 
| ТабЧасть.Номенклатура, 
| ТабЧасть.СерияНоменклатуры, 
| ТабЧасть.Склад, 
| ТабЧасть.ХарактеристикаНоменклатуры 
| |ИМЕЮЩИЕ 
| КОЛИЧЕСТВО(ТабЧасть.НомерСтроки) > 1 
|; 

|//////////////////////////////////////////////////////////////////////////////// 
|ВЫБРАТЬ 
| ТабЧасть.Ссылка, 
| ТабЧасть.Качество, 
| ТабЧасть.Номенклатура, 
| ТабЧасть.СерияНоменклатуры, 
| ТабЧасть.Склад, 
| ТабЧасть.ХарактеристикаНоменклатуры, 
| Док.Товары.( 
| НомерСтроки, 
| Качество, 
| Номенклатура, 
| СерияНоменклатуры, 
| Склад, 
| ХарактеристикаНоменклатуры 
| ) КАК ДублиСтрок 
|ИЗ 
| (ВЫБРАТЬ 
| Док.Товары.( 
| НомерСтроки, 
| Качество, 
| Номенклатура, 
| СерияНоменклатуры, 
| Склад, 
| ХарактеристикаНоменклатуры 
| ) КАК Товары 
| ИЗ 
| Документ.РеализацияТоваровУслуг КАК Док 
| ГДЕ 
| Док.Ссылка = &Ссылка 
| И (Док.Товары.Качество, Док.Товары.Номенклатура, Док.Товары.СерияНоменклатуры, Док.Товары.Склад, Док.Товары.ХарактеристикаНоменклатуры) В 
| (ВЫБРАТЬ 
| ВТ.Качество, 
| ВТ.Номенклатура, 
| ВТ.СерияНоменклатуры, 
| ВТ.Склад, 
| ВТ.ХарактеристикаНоменклатуры 
| ИЗ 
| ВТ_ТабЧасть КАК ВТ)) КАК Док 
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ТабЧасть КАК ТабЧасть 
| ПО (ИСТИНА) 
|ГДЕ 
| Док.Товары.Качество = ТабЧасть.Качество 
| И Док.Товары.Номенклатура = ТабЧасть.Номенклатура 
| И Док.Товары.СерияНоменклатуры = ТабЧасть.СерияНоменклатуры 
| И Док.Товары.Склад = ТабЧасть.Склад 
| И Док.Товары.ХарактеристикаНоменклатуры = ТабЧасть.ХарактеристикаНоменклатуры 

|УПОРЯДОЧИТЬ ПО 
| ТабЧасть.МинНомерСтроки, 
| Док.Товары.НомерСтроки”;
ТЗ_Результат = Запрос.Выполнить().Выгрузить(); 
Для Каждого СтрТЗ Из ТЗ_Результат Цикл 
Сообщить(СтрШаблон(ШаблонОшибки, СтрСоединить(СтрТЗ.ДублиСтрок.ВыгрузитьКолонку(“НомерСтроки”), “, “))); 
КонецЦикла;

Главное отличие от предыдущего варианта: обход результата запроса осуществляется линейным способом. Т.е. при изменении состава ключевых полей меняется только текст запроса.

Несколько комментариев по поводу запроса.

Работа с табличными частями в качестве полей выборки накладывает ряд ограничений на выполнение запросов. Во-первых, это работа с временными таблицами. Т.е. нельзя помещать такие объекты во временные таблицы, но никто не мешает использовать вложенные запросы. Во-вторых, это соединения таблиц. Мне требовалось написать такой запрос, который бы возвратил мне наборы ключевых полей по которым имеются дубли, и многострочный объект, содержащий все строки с таким же набором ключевых полей. Обычные соединения (ВНУТРЕННЕЕ, ЛЕВОЕ, ПРАВОЕ, ПОЛНОЕ) возвращают всю табличную часть, что, в общем, правильно – для части объекта условие соединения же выполняется? Выполняется. Ну тогда и получите всю табличную часть. Искомый результат, как оказалось, достигается декартовым произведением. Мне как-то претит видеть в тексте запроса перечисление таблиц через запятую, поэтому я все декартовы произведения всегда реализую через «… СОЕДИНЕНИЕ … ПО (ИСТИНА)». Чтобы ограничить мсье Декарта в аппетитах (и повысить производительность), применяются дополнительные ограничения.

Теперь о производительности. Производились тестовые замеры каждого из вариантов на документе с большим количеством строк в табличной части. Количество строк в тестируемом документе: 47 817, 4 комбинации ключевых полей с дублями по 2, 2, 3 и 4 строки. Результаты замеров:

Вариант 1) 0:00:00.078 сек.

Вариант 2) 0:00:00.265 сек.

Вариант 3) 0:00:01.513 сек.

Как видим, третий вариант, как и ожидалось, самый медленный, но он же является самым удобным в перспективе модификации.

Прилагаемая обработка является демонстратором третьего варианта. Работает она и в обычном, и в управляемом режимах. Внешний вид и функционал полностью одинаков.

1.png

1) Выбираем вид объекта: Документ или Справочник.

2) Выбираем тип объекта: Какой именно документ или справочник.

3) Выбираем табличную часть объекта.

4) Определяем состав ключевых полей в специальном диалоге

2.png

5) Если мы хотим, то можем указать ссылку на объект, чтобы проверить его на наличие дублей.

6) Если активен флажок «Сгенерировать и показать код для проверки на дубли», то будет сгенерирован программный код для выполнения проверки на дубли строк с имеющимися настройками.

Автор статьи:


Эксперт по технологическим вопросам ИнфоСофт

 +7 

   

Распечатать

Универсальная процедура проверки на дубли строк в табличной части документа

Код 1C v 8.2 УП

 //Процедура проверяет наличие в ТЧ дублей строк по указанным реквизитам
//ДокументСсылка - ссылка на проверяемый документ
//ИмяТабЧасти - имя проверяемой табличной части (строка)
//ПроверяемыеРеквизиты - перечень проверяемых реквизитов (строка, реквизиты разделяются запятой)
Процедура ПроверимДублиСтрок(ДокументСсылка,ИмяТабЧасти,ПроверяемыеРеквизиты,Отказ)

ТаблицаДокумента = ДокументСсылка[ИмяТабЧасти].Выгрузить();
ТаблицаДокумента.Колонки.Добавить("КвоПроверкаДублей");
ТаблицаДокумента.ЗаполнитьЗначения(1, "КвоПроверкаДублей");

ТаблицаДокумента.Свернуть(ПроверяемыеРеквизиты,"КвоПроверкаДублей");

Для Каждого ТекущаяСтрока Из ТаблицаДокумента Цикл
Если ТекущаяСтрока.КвоПроверкаДублей > 1 Тогда
РеквизитыДляСообщения = "";
Стр = СокрЛП(ПроверяемыеРеквизиты);
Пока Найти(Стр,",") > 0 Цикл
НаимРеквизита = СокрЛП(Лев(Стр,Найти(Стр,",") - 1));
Стр = Прав(Стр,СтрДлина(Стр) - Найти(Стр,","));
РеквизитыДляСообщения = РеквизитыДляСообщения+ТекущаяСтрока[НаимРеквизита]+", ";
КонецЦикла;
РеквизитыДляСообщения = РеквизитыДляСообщения+ТекущаяСтрока[СокрЛП(Стр)];

Сообщить("Информация "+РеквизитыДляСообщения+" введена несколько раз в табличную часть, проведение невозможно!");
Отказ = Истина;
КонецЕсли;
КонецЦикла;

КонецПроцедуры

Пример обращения к процедуре проверки

Код 1C v 8.2 УП

 // Процедура - обработчик события ОбработкаПроверкиЗаполнения объекта.
//
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)

Если Запасы.Количество() = 0 Тогда
Возврат;
КонецЕсли;

ПроверимДублиСтрок(Ссылка, "Запасы", "Номенклатура, АлгоритмФормированияШК,Регион", Отказ);

КонецПроцедуры

Да, в списке реквизитов добавленные случайно пробелы игнорируются )
Процедуру можно добавить в общий модуль и обращаться к ней из любого документа

Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)

   Если Товары.Количество() = 0 Тогда
       Возврат;
   КонецЕсли;

   ПроверитьДублиСтрок(Ссылка, “Товары”, “Номенклатура, ШтрихКод”, Отказ);

КонецПроцедуры

//Процедура проверяет наличие в ТЧ дублей строк по указанным реквизитам
//ДокументСсылка       – ссылка на проверяемый документ
//ИмяТабЧасти          – имя проверяемой табличной части (строка)
//ПроверяемРеквизиты – перечень проверяемых реквизитов (строка, реквизиты разделяются запятой)
Процедура ПроверитьДублиСтрок(ДокументСсылка, ИмяТабЧасти, ПроверяемРеквизиты, Отказ)

   ТаблДок = ДокументСсылка[ИмяТабЧасти].Выгрузить();
   ТаблДок.Колонки.Добавить(“КоличПроверкаДублей”);
   ТаблДок.ЗаполнитьЗначения(1, “КоличПроверкаДублей”);
   ТаблДок.Свернуть(ПроверяемРеквизиты,“КоличПроверкаДублей”);

   Для Каждого ТекСтрока Из ТаблДок Цикл
       Если ТекСтрока.КоличПроверкаДублей > 1 Тогда
           ДляСообщения = “”;
           СтрРекв = СокрЛП(ПроверяемРеквизиты);
           Пока Найти(СтрРекв,“,”) > 0 Цикл
               НаимРеквизита = СокрЛП(Лев(СтрРекв,Найти(СтрРекв,“,”) – 1));
               СтрРекв = Прав(СтрРекв,СтрДлина(СтрРекв) – Найти(СтрРекв,“,”));
               ДляСообщения = ДляСообщения+ТекСтрока[НаимРеквизита]+“, “;
           КонецЦикла;
           ДляСообщения = ДляСообщения+ТекСтрока[СокрЛП(СтрРекв)];

           Сообщение = Новый СообщениеПользователю;
           Сообщение.Текст = “В разделе “ + ТекСтрока.Раздел + ” одинаковые значения: “ + ДляСообщения + “, сохранение невозможно”;
           Сообщение.Сообщить();

           Отказ = Истина;
       КонецЕсли;
   КонецЦикла;

КонецПроцедуры

Добавить комментарий