Как в 1С найти документы с одинаковой табличной частью за одну дату?

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

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

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

В чем сложность прямого сравнения табличных частей в запросе?

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

Кроме того, в языке запросов платформы 1С нет встроенных средств агрегации строк (по аналогии со строковой конкатенацией в реляционных СУБД), поэтому «склеить» всю табличную часть в одну строку прямо в тексте запроса невозможно.

Способ 1. Поиск дублей через равенство количества строк и совпадений (в запросе)

Рассмотрим элегантный математический алгоритм. Два документа считаются дубликатами по составу табличной части, если одновременно выполняются три условия:

  1. Количество строк в первом документе равно $N$.
  2. Количество строк во втором документе равно $N$.
  3. Количество строк первого документа, совпавших со строками второго документа по всем ключевым колонкам, также равно $N$.

Посмотрим на практическую реализацию этого алгоритма с помощью пакетного запроса:


ВЫБРАТЬ
    ЗаказПокупателяТовары.Ссылка КАК Ссылка,
    ЗаказПокупателяТовары.Номенклатура КАК Номенклатура,
    ЗаказПокупателяТовары.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
    ЗаказПокупателяТовары.СерияНоменклатуры КАК СерияНоменклатуры,
    ЗаказПокупателяТовары.Цена КАК Цена,
    ЗаказПокупателяТовары.Количество КАК Количество,
    1 КАК СтрокВОригинале
ПОМЕСТИТЬ ВТ_Оригиналы
ИЗ
    Документ.ЗаказПокупателя.Товары КАК ЗаказПокупателяТовары
ГДЕ
    НАЧАЛОПЕРИОДА(ЗаказПокупателяТовары.Ссылка.Дата, ДЕНЬ) = НАЧАЛОПЕРИОДА(&ДатаАнализа, ДЕНЬ)
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Оригиналы.Ссылка КАК Ссылка,
    ПотенциальныеДубли.Ссылка КАК Дубль,
    СУММА(Оригиналы.СтрокВОригинале) КАК СтрокВОригинале,
    СУММА(ПотенциальныеДубли.СтрокВОригинале) КАК СтрокВДубле,
    СУММА(1) КАК Совпадений
ПОМЕСТИТЬ ВТ_Сравнение
ИЗ
    ВТ_Оригиналы КАК Оригиналы
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Оригиналы КАК ПотенциальныеДубли
        ПО Оригиналы.Ссылка <> ПотенциальныеДубли.Ссылка
            И Оригиналы.Номенклатура = ПотенциальныеДубли.Номенклатура
            И Оригиналы.ХарактеристикаНоменклатуры = ПотенциальныеДубли.ХарактеристикаНоменклатуры
            И Оригиналы.СерияНоменклатуры = ПотенциальныеДубли.СерияНоменклатуры
            И Оригиналы.Цена = ПотенциальныеДубли.Цена
            И Оригиналы.Количество = ПотенциальныеДубли.Количество
СГРУППИРОВАТЬ ПО
    Оригиналы.Ссылка,
    ПотенциальныеДубли.Ссылка
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Сравнение.Ссылка КАК ОсновнойДокумент,
    ВТ_Сравнение.Дубль КАК ДублирующийДокумент
ИЗ
    ВТ_Сравнение КАК ВТ_Сравнение
ГДЕ
    ВТ_Сравнение.СтрокВОригинале = ВТ_Сравнение.СтрокВДубле
    И ВТ_Сравнение.СтрокВДубле = ВТ_Сравнение.Совпадений

Разберем по шагам, как работает этот запрос:

  1. Формирование выборки: Во временную таблицу ВТ_Оригиналы помещаются все строки табличных частей за указанный день. Поле 1 КАК СтрокВОригинале служит счетчиком для последующего суммирования.
  2. Сравнение пар документов: Таблица соединяется сама с собой по условию несовпадения ссылок документов и равенства товарных позиций. В результате группировки вычисляется число строк в каждом из документов и общее количество совпавших позиций.
  3. Итоговая фильтрация: В результирующую выборку попадают только те пары документов, у которых размеры табличных частей равны между собой и в точности равны числу сопоставленных строк.

Способ 2. Хеширование состава табличной части

Если документов за период создается много, самосоединение больших объемов данных в запросе может существенно нагружать СУБД. В таких ситуациях наиболее производительным решением является вычисление контрольной суммы (хеша) табличной части.

Рассмотрим, как это работает на встроенном языке 1С:

  1. При проведении или записи документа строки табличной части сортируются (например, по номенклатуре и характеристике), чтобы исключить влияние порядка ввода.
  2. Для каждой строки формируется строковое представление: Номенклатура + Характеристика + Количество + Цена.
  3. Полученная длинная строка передается в объект ХешированиеДанных (с алгоритмом ХешФункция.MD5 или ХешФункция.SHA256).
  4. Полученный хеш записывается в реквизит шапки документа или в специальный регистр сведений.

Посмотрим на пример вычисления хеша табличной части:


Хеширование = Новый ХешированиеДанных(ХешФункция.MD5);
ТаблицаТоваров = ДокументОбъект.Товары.Выгрузить();
ТаблицаТоваров.Сортировать("Номенклатура, ХарактеристикаНоменклатуры, Цена");

Для Каждого Стр Из ТаблицаТоваров Цикл
    СтрокаДанных = Строка(Стр.Номенклатура.УникальныйИдентификатор()) + ";"
                 + Строка(Стр.Цена) + ";"
                 + Строка(Стр.Количество) + "|";
    Хеширование.Добавить(СтрокаДанных);
КонецЦикла;

ХешТабличнойЧасти = Хеширование.ХешСумма;

При таком подходе поиск дублей сводится к элементарному запросу с группировкой по реквизитам Склад, Дата и ХешТабличнойЧасти с условием ИМЕЮЩИЕ КОЛИЧЕСТВО(Ссылка) > 1, что выполняется мгновенно даже на миллионах записей.

Способ 3. Обработка через Таблицу Значений

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

Проанализируем последовательность действий:

  1. Выбираем документы за период с их строками.
  2. Группируем строки по каждому документу, конкатенируя строковые идентификаторы товаров в одну строку внутри цикла.
  3. Помещаем полученные уникальные ключи документов во временную структуру или таблицу значений и находим повторения с помощью метода Свернуть().

Как предотвратить появление дублей при интеграции?

Выясним причину дублирования на этапе архитектуры обмена. Чтобы исключить повторное создание заявок при повторных сетевых запросах или сбоях каналов связи, рекомендуется внедрять следующие механизмы:

  1. Регистр внешних идентификаторов: Создайте независимый регистр сведений, где ключевым измерением будет ИдентификаторЗаявкиИнтернетМагазина, а ресурсом — Ссылка на созданный заказ. Перед созданием проверяйте наличие записи в регистре.
  2. Транзакционные блокировки: При получении пакета устанавливайте управляемую блокировку через БлокировкаДанных по внешнему идентификатору заявки. Это предотвратит параллельную запись одного и того же заказа несколькими фоновыми заданиями.
← На главную