Top.Mail.Ru
Заказать консультацию
специалиста 1С
Отправить заявку

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

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

26.05.2018
31984
Время прочтения - 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) Если активен флажок «Сгенерировать и показать код для проверки на дубли», то будет сгенерирован программный код для выполнения проверки на дубли строк с имеющимися настройками.

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

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

Владимир Ротанин 

Оригинал статьи: infostart.ru/public/811035/


Заказать консультацию специалиста 1С
Оставьте заявку и наши эксперты проконсультируют вас по данной статье.
Отправить заявку

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

Рассказать друзьям
Для разработчиков 1С
Вам может быть интересно: