Как сравнить данные табличной части с остатками регистра в 1С

Программист 1С v8.3 (Обычные формы) Управленческий учет IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С:Предприятие часто возникает задача сверки двух наборов данных: например, содержимого табличной части документа или обработки с фактическими остатками в регистре накопления (таком как ПартииТоваровНаСкладах). Рассмотрим подробнее, как правильно подготовить данные и составить эффективный запрос для выявления расхождений по номенклатуре и количеству.

Подготовка данных: передача таблицы значений в запрос

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

  1. Типизация колонок. Если таблица значений формируется программно, обязательно проверяем наличие явного описания типов колонок через ОписаниеТипов. При выгрузке готовой табличной части методом ТабличнаяЧасть.Выгрузить() типизация сохраняется автоматически.
  2. Использование пакетного запроса. Чтобы не создавать отдельный объект МенеджерВременныхТаблиц в кодовой части, объединим все этапы запроса в один пакет, разделяя части точкой с запятой.
  3. Сворачивание дублей. Если в исходной табличной части одна и та же номенклатура встречается в нескольких строках, ее необходимо предварительно сгруппировать, чтобы избежать дублирования строк при сопоставлении.

Посмотрим на пример базовой передачи таблицы значений в параметры запроса и помещения ее во временную таблицу:


// Создаем запрос и передаем выгруженную табличную часть
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ТзДанные", ТабличнаяЧасть.Выгрузить());
Запрос.УстановитьПараметр("Период", ТекущаяДата());

Запрос.Текст = 
"ВЫБРАТЬ
|	Тз.Номенклатура КАК Номенклатура,
|	СУММА(Тз.Количество) КАК Количество
|ПОМЕСТИТЬ ВтИсходныеДанные
|ИЗ
|	&ТзДанные КАК Тз
|СГРУППИРОВАТЬ ПО
|	Тз.Номенклатура
|ИНДЕКСИРОВАТЬ ПО
|	Номенклатура;
|////////////////////////////////////////////////////////////////////////////////";

Способ 1. Метод разнознакового объединения («Дельта»)

Проанализируем наиболее надежный и быстрый способ нахождения расхождений — объединение таблиц с обратными знаками количественных показателей. Преимущество данного подхода заключается в отсутствии сложных условий сопоставления полей со значением NULL.

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

Разберем готовый пример пакетного запроса:


Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ТзДанные", ТаблицаДляСверки);
Запрос.УстановитьПараметр("МоментВремени", ТекущаяДата());

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

РезультатЗапроса = Запрос.Выполнить().Выгрузить();

Обратите внимание: в параметрах виртуальной таблицы регистра остатков обязательно передается условие отбора по номенклатуре из временной таблицы. Это исключает сканирование всей таблицы остатков базы данных и снижает нагрузку на СУБД.

Способ 2. Сравнение через полное соединение

Рассмотрим альтернативный способ — сопоставление через ПОЛНОЕ СОЕДИНЕНИЕ. Он удобен, когда необходимо явно видеть структуру исходных и учетных данных в одной строке.

При использовании этого метода важно правильно обрабатывать поля, в которых может возникнуть значение NULL (в случаях, когда позиция есть в документе, но отсутствует на остатках регистра, или наоборот):

  1. Все ключевые поля аналитики в секции ВЫБРАТЬ оборачиваем в функцию ЕСТЬNULL(Таб1.Поле, Таб2.Поле).
  2. Все числовые поля для вычисления разницы приводим к нулю с помощью ЕСТЬNULL(Поле, 0).
  3. Условие отбора расхождений задаем в секции ГДЕ с явным сравнением разницы: ГДЕ ЕСТЬNULL(Таб1.Количество, 0) <> ЕСТЬNULL(Таб2.Количество, 0).

Посмотрим на пример запроса с полным соединением:


Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ТзДанные", ТаблицаДляСверки);
Запрос.УстановитьПараметр("МоментВремени", ТекущаяДата());

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

Способ 3. Программное сравнение коллекций в коде

Если объем данных невелик или сравнение выполняется в типовых конфигурациях (например, в УПП), можно воспользоваться встроенными механизмами общих модулей (функции вида СравнитьТаблицыЗначений) либо сопоставить строки вручную через объект Соответствие:

  1. Сворачиваем обе таблицы методом ТаблицаЗначений.Свернуть("Номенклатура", "Количество").
  2. Заполняем Соответствие, где ключом выступает ссылка на номенклатуру, а значением — количество из регистра.
  3. Обходим строки первой таблицы и сравниваем показатели, фиксируя несовпадающие позиции.

Для больших объемов учетных данных предпочтительным и наиболее быстродействующим всегда остается сравнение в запросе с помощью метода дельты через ОБЪЕДИНИТЬ ВСЕ.

← На главную