Как убрать дублирование строк при сопоставлении данных двух документов в запросе 1С

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

При разработке отчетов или механизмов контроля (особенно в системах маркировки МДЛП) часто возникает необходимость сопоставить данные двух документов: например, «Отпуска товаров в отделение» и «Уведомления о выдаче». Основная сложность заключается в том, что связь по номенклатуре и серии не всегда является уникальной, что приводит к «размножению» строк (эффекту декартова произведения) при использовании обычных соединений ЛЕВОЕ СОЕДИНЕНИЕ или ПОЛНОЕ СОЕДИНЕНИЕ. Разберем подробно, как решить эту проблему несколькими способами.

Причина возникновения дублей и анализ ситуации

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

Проанализируем предложенные варианты решения проблемы, от простых соединений до наиболее надежной методики объединения.

Способ №1: Использование ОБЪЕДИНИТЬ ВСЕ и СГРУППИРОВАТЬ (Рекомендуемый)

Этот метод считается эталонным в 1С для задач сопоставления. Вместо того чтобы «склеивать» таблицы боками, мы помещаем их друг под друга, разделяя ресурсы (количества) по разным колонкам, а затем «схлопываем» их через группировку. Рассмотрим этот подход по шагам.

Сначала мы выбираем данные из первого документа (Отпуск) и создаем «пустую» колонку для данных второго документа. Затем добавляем данные из второго документа (Уведомление) через ОБЪЕДИНИТЬ ВСЕ, оставляя «пустой» колонку первого документа.


ВЫБРАТЬ
    ВложенныйЗапрос.Ссылка КАК ДокументОснование,
    ВложенныйЗапрос.Номенклатура КАК Номенклатура,
    ВложенныйЗапрос.Серия КАК Серия,
    СУММА(ВложенныйЗапрос.КоличествоОтпуск) КАК КоличествоОтпуск,
    СУММА(ВложенныйЗапрос.КоличествоУведомление) КАК КоличествоУведомление
ИЗ
    (ВЫБРАТЬ
        ОтпускТовары.Ссылка КАК Ссылка,
        ОтпускТовары.Номенклатура КАК Номенклатура,
        ОтпускТовары.СерияНоменклатуры КАК Серия,
        ОтпускТовары.Количество КАК КоличествоОтпуск,
        0 КАК КоличествоУведомление
    ИЗ
        Документ.ОтпускТоваровВОтделение.Товары КАК ОтпускТовары
    ГДЕ
        ОтпускТовары.Ссылка = &Ссылка

    ОБЪЕДИНИТЬ ВСЕ

    ВЫБРАТЬ
        УведомлениеТовары.Ссылка.Основание,
        УведомлениеТовары.Номенклатура,
        УведомлениеТовары.Серия,
        0,
        УведомлениеТовары.Количество
    ИЗ
        Документ.УведомлениеОВыдачеВОтделенияМДЛП.Товары КАК УведомлениеТовары
    ГДЕ
        УведомлениеТовары.Ссылка.Основание = &Ссылка) КАК ВложенныйЗапрос

СГРУППИРОВАТЬ ПО
    ВложенныйЗапрос.Ссылка,
    ВложенныйЗапрос.Номенклатура,
    ВложенныйЗапрос.Серия

Преимущества этого метода:

  1. Никакие данные не теряются (даже если в одном из документов нет соответствующей серии).
  2. Исключено дублирование количеств из-за соединений «многие-ко-многим».
  3. Запрос работает быстро, так как группировка — это очень эффективная операция для СУБД.

Способ №2: ПОЛНОЕ СОЕДИНЕНИЕ с использованием ЕСТЬNULL

Если вам критично использовать именно соединения (например, когда нужно выводить специфические реквизиты каждого документа в разных колонках), следует использовать ПОЛНОЕ СОЕДИНЕНИЕ. Это позволит увидеть строки, которые есть в первом документе, но отсутствуют во втором, и наоборот.

Важный нюанс: при полном соединении поля связи могут содержать NULL, если соответствие не найдено. Поэтому в полях ВЫБРАТЬ и в условии ГДЕ обязательно используйте функцию ЕСТЬNULL.


ВЫБРАТЬ
    ЕСТЬNULL(ВТ_Отпуска.Номенклатура, ВТ_Уведомления.Номенклатура) КАК Номенклатура,
    ЕСТЬNULL(ВТ_Отпуска.СерияНоменклатуры, ВТ_Уведомления.Серия) КАК Серия,
    ЕСТЬNULL(ВТ_Отпуска.Количество, 0) КАК КоличествоОтпуска,
    ЕСТЬNULL(ВТ_Уведомления.Количество, 0) КАК КоличествоВУведомлении
ИЗ
    ВТ_Отпуска КАК ВТ_Отпуска
        ПОЛНОЕ СОЕДИНЕНИЕ ВТ_Уведомления КАК ВТ_Уведомления
        ПО ВТ_Отпуска.Номенклатура = ВТ_Уведомления.Номенклатура
            И ВТ_Отпуска.СерияНоменклатуры = ВТ_Уведомления.Серия

Рассмотрим подробнее проблему дублей в этом варианте. Если в ВТ_Отпуска будет две строки с одной серией, а в ВТ_Уведомления — одна, вы получите две строки в результате, но количество из уведомления «размножится» на обе строки. Чтобы этого избежать, данные в исходных временных таблицах должны быть предварительно сгруппированы по ключам связи (Номенклатура + Серия).

Способ №3: Решение через АВТОНОМЕРЗАПИСИ (для точного сопоставления строк)

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

Выясним причину, зачем это нужно: если в документе отпуска две строки «Спирт, серия 001, 5 литров», и в уведомлении тоже две такие же строки, то обычное соединение даст 4 строки. Если же мы пронумеруем строки в каждом документе (1 и 2) и добавим НомерСтроки в условие связи, мы получим ровно 2 строки в результате.


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

// При соединении используем номер строки как часть ключа
ВЫБРАТЬ ...
ИЗ ВТ_Отпуска_Индекс КАК Т1
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Уведомления_Индекс КАК Т2
ПО Т1.Номенклатура = Т2.Номенклатура
    И Т1.Серия = Т2.Серия
    И Т1.НомерСтроки = Т2.НомерСтроки

Специфика работы с сериями и МДЛП

Разберем ситуацию, когда в одном документе серия заполнена, а во втором — нет. Это часто бывает, если документ-основание заполнен частично или серии были изменены в процессе маркировки. Чтобы запрос не терял такие данные:

Итоги и рекомендации

Для корректного решения задачи мы рекомендуем придерживаться следующего алгоритма:

  1. Всегда предварительно группируйте данные из табличных частей во временных таблицах, прежде чем выполнять соединения. Группировать нужно по всем полям, которые составляют уникальный ключ (Ссылка, Номенклатура, Серия, СтавкаНДС и т.д.).
  2. Если ваша цель — отчет-сверка, используйте метод объединения (Способ №1). Это самый безопасный вариант, гарантирующий отсутствие дублей.
  3. Если вы используете СКД (Систему компоновки данных), попробуйте реализовать сопоставление через связи наборов данных. СКД умеет корректно объединять данные из разных источников, автоматически обрабатывая NULL и группировки.
  4. При использовании ПОЛНОЕ СОЕДИНЕНИЕ всегда оборачивайте выбираемые поля в ЕСТЬNULL, чтобы результат был читаемым и пригодным для дальнейших вычислений.
← На главную