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

04.04.18

Разработка - Запросы

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

Скачать исходный код

Наименование Файл Версия Размер
Проверка на наличие дублирующихся строк в табличных частях
.epf 17,41Kb
9
.epf 1 17,41Kb 9 Скачать

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

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

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

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) Выбираем вид объекта: Документ или Справочник.

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

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

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

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

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

дубли дубли строк дублирующиеся строки

См. также

Infostart Toolkit: Инструменты разработчика 1С 8.3 на управляемых формах

Инструментарий разработчика Роли и права Запросы СКД Платформа 1С v8.3 Управляемые формы Запросы Система компоновки данных Конфигурации 1cv8 Платные (руб)

Набор инструментов программиста и специалиста 1С для всех конфигураций на управляемых формах. В состав входят инструменты: Консоль запросов, Консоль СКД, Консоль кода, Редактор объекта, Анализ прав доступа, Метаданные, Поиск ссылок, Сравнение объектов, Все функции, Подписки на события и др. Редактор запросов и кода с раскраской и контекстной подсказкой. Доработанный конструктор запросов тонкого клиента. Продукт хорошо оптимизирован и обладает самым широким функционалом среди всех инструментов, представленных на рынке.

10000 руб.

02.09.2020    129733    700    390    

752

Как посмотреть итоговый запрос в отчете СКД

Запросы СКД Система компоновки данных Россия Бесплатно (free)

Часто при разработке отчетов в СКД возникает ситуация, когда не совсем понятно, почему отчет выводит не те данные, которые нужны, либо не выводит вовсе. Возникает потребность увидеть конечный запрос, который формирует СКД. Как это сделать, рассмотрим в этой статье.

15.05.2024    2940    implecs_team    4    

34

Пропорциональное распределение в запросе с использованием АвтоНомерЗаписи()

Запросы Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Часто поступают задачи по произвольному распределению общих сумм. После распределения иногда пропадают копейки. Суть решения добавить АвтоНомерЗаписи() в ВТ распределения, и далее используя функции МАКСИМУМ или МИНИМУМ можем положить разницу копеек в первую или последнюю строку знаменателя распределения.

11.04.2024    2619    andrey_sag    10    

32

Для чего используют конструкцию запроса "ГДЕ ЛОЖЬ" в СКД на примере конфигурации 1С:ERP

Запросы СКД Платформа 1С v8.3 Запросы Система компоновки данных 1С:ERP Управление предприятием 2 Бесплатно (free)

В типовых конфигурациях разработчики компании 1С иногда используют в отчетах, построенных на СКД, такую конструкцию, как "ГДЕ ЛОЖЬ". Такая конструкция говорит о том, что данные в запросе не будут получены совсем. Для чего же нужен тогда запрос?

13.02.2024    6300    KawaNoNeko    23    

26

Набор-объект для СКД по тексту или запросу

Запросы СКД Платформа 1С v8.3 Управляемые формы Конфигурации 1cv8 Абонемент ($m)

Есть список полей в виде текста, или запрос - закидываем в набор СКД.

1 стартмани

31.01.2024    2291    2    Yashazz    0    

32

Запрос 1С copilot

Инструментарий разработчика Запросы Платформа 1С v8.3 Управляемые формы Конфигурации 1cv8 Бесплатно (free)

Пишем на человеческом языке, что нам надо, и получаем текст запроса на языке 1С. Используются большие языковые модели (LLM GPT) от OpenAI или Яндекс на выбор.

15.01.2024    7111    40    mkalimulin    32    

53

PrintWizard: поддержка представлений ЗУП в конструкторе

Инструментарий разработчика Запросы Платформа 1С v8.3 Бесплатно (free)

Одной из интересных задач, стоящих в процессе разработки, была поддержка механизма представлений в ЗУП. Но не просто возможность исполнения запросов с ними. Основная проблема была в том, чтобы с ними было удобно работать, а именно: создавать, модифицировать и отлаживать. Кратко о том, что в итоге получилось...

14.12.2023    2015    vandalsvq    7    

29

Консоль запросов УФ 8.3.2.24.12 (мод от Dr.Zombi)

Инструментарий разработчика Запросы Платформа 1С v8.3 Управляемые формы Запросы Россия Абонемент ($m)

Работа с запросом и СКД, Полная поддержка пакетных запросов, временных таблиц. Главное скорость отладки запроса и данных, а красота вторична.

1 стартмани

07.12.2023    3483    52    DrZombi    54    

21
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. klinval 339 09.04.18 15:05 Сейчас в теме
Выгружаешь в таблицы значений (например ЭтотОбъект.Товары.Выгрузить()) и сравниваешь любым методом из статьи Ильдаровича: https://infostart.ru/public/326983/
2. RotaninV 23 09.04.18 18:19 Сейчас в теме
(1) Тоже один из вариантов, имеющих право на жизнь. Выбирайте тот, который устраивает вас.
Мой вариант интересен в том числе как способ получения нужного набора строк табличной части в одном поле выборки.
Оставьте свое сообщение