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

04.04.18

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

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

Скачать файл

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Наименование По подписке [?] Купить один файл
Проверка на наличие дублирующихся строк в табличных частях
.epf 17,41Kb ver:1
11
11 Скачать (1 SM) Купить за 1 850 руб.

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

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

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

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

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

См. также

SALE! 15%

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

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

10000 руб.

02.09.2020    159690    876    399    

862

Запросы Программист Бесплатно (free)

Увидел cheatsheet по SQL и захотелось нарисовать подобное, но про запросы.

18.10.2024    9950    sergey279    18    

64

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

Столкнулся с интересной ситуацией, которую хотел бы разобрать, ввиду её неочевидности. Речь пойдёт про использование функции запроса АВТОНОМЕРЗАПИСИ() и проблемы, которые могут возникнуть.

11.10.2024    5206    XilDen    36    

80

Запросы Программист Запросы Бесплатно (free)

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

16.08.2024    7945    user1840182    5    

28

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

Рассмотрим быстрый алгоритм поиска дублей с использованием hash функции по набору полей шапки и табличных частей.

08.07.2024    2406    ivanov660    9    

22

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

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

15.05.2024    8730    implecs_team    6    

47

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

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

11.04.2024    3396    andrey_sag    10    

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