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