Почему в запросе 1С двоятся строки номенклатуры и как это исправить?

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

При разработке механизмов выгрузки товаров в торговое оборудование (например, ККМ Offline) разработчики часто сталкиваются с ситуацией, когда запрос возвращает больше строк, чем содержится в справочнике Справочник.Номенклатура. Допустим, при общем количестве позиций в 5 000 строк выборка возвращает 5 001 позицию. Разберем по шагам, почему возникают такие дубли и какими способами их можно гарантированно устранить.

Основные причины появления дублей

Проанализируем структуру типового запроса выгрузки: он состоит из объединения нескольких источников (регистр Штрихкоды, регистр КодыВесовогоТовара и непосредственно справочник Номенклатура), после чего результат соединяется с регистром сведений ТоварыНаККМ через ЛЕВОЕ СОЕДИНЕНИЕ по условию отсутствия записи (ГДЕ ТоварыНаККМ.Номенклатура ЕСТЬ NULL).

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

  1. Разные единицы измерения или характеристики у одной номенклатуры. Конструкция ВЫБРАТЬ РАЗЛИЧНЫЕ или оператор ОБЪЕДИНИТЬ сравнивают всю строку целиком. Если для одного товара в регистре Штрихкоды указана упаковка, а в ветке справочника берется ЕдиницаХраненияОстатков (штука), система воспримет эти строки как две абсолютно разные записи.
  2. Несовпадение типов (NULL и пустая ссылка). Если в регистре сведений незаполненная характеристика хранится как NULL (например, из-за составного типа данных), а в ветке номенклатуры подставляется ЗНАЧЕНИЕ(Справочник.ХарактеристикиНоменклатуры.ПустаяСсылка), то условие соединения ТоварыНаККМ.ХарактеристикаНоменклатуры = Ном.ХарактеристикаНоменклатуры не сработает, так как в СУБД NULL не равен пустой ссылке.
  3. Неполный состав полей в условии соединения. Если в регистре ТоварыНаККМ измерением является Код (PLU-код), а соединение идет только по номенклатуре, характеристике, серии и единице, одна строка левой таблицы может соединиться с несколькими записями правой, что приведет к размножению строк.

Способ 1. Локализация ошибки через отладочное условие и список полей

Выясним причину дублирования с помощью изоляции левого соединения. Для этого добавим в условие связи заведомо ложное выражение:


ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ТоварыНаККМ КАК ТоварыНаККМ
    ПО ЛОЖЬ
    И ТоварыНаККМ.Номенклатура = Ном.Номенклатура

Если после этого количество строк в выборке уменьшилось до исходных 5 000, значит, дублирование вносит именно таблица ТоварыНаККМ. Если же дубль остался, причина кроется во вложенном запросе объединения Ном.

Чтобы увидеть, какое именно поле отличается у задвоенной номенклатуры, развернем конструкцию Ном.* в явный список полей:


ВЫБРАТЬ РАЗЛИЧНЫЕ
    Ном.Номенклатура КАК Номенклатура,
    Ном.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
    Ном.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
    Ном.СерияНоменклатуры КАК СерияНоменклатуры,
    СпрКассы.Ссылка КАК Касса
ИЗ
    ВТ_Номенклатура КАК Ном

Способ 2. Перевод запроса на пакетную обработку с временными таблицами

Использование глубоких вложенных подзапросов затрудняет отладку и может приводить к неоптимальным планам выполнения в СУБД. Перепишем запрос с использованием временных таблиц ПОМЕСТИТЬ и явной индексацией полей связи:


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

ОБЪЕДИНИТЬ

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

ОБЪЕДИНИТЬ

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

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
    ВТ.Номенклатура КАК Номенклатура,
    ВТ.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
    ВТ.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
    ВТ.СерияНоменклатуры КАК СерияНоменклатуры
ПОМЕСТИТЬ ВТ_УникальнаяНоменклатура
ИЗ
    ВТ_ИсходныеДанные КАК ВТ
ИНДЕКСИРОВАТЬ ПО
    Номенклатура,
    ХарактеристикаНоменклатуры,
    СерияНоменклатуры,
    ЕдиницаИзмерения
;

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

Способ 3. Замена левого соединения фильтром «ГДЕ НЕ ... В»

Если требуется полностью исключить влияние структуры регистра ТоварыНаККМ на количество возвращаемых строк, заменим ЛЕВОЕ СОЕДИНЕНИЕ на проверку отсутствия кортежа значений через подзапрос:


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

Такой подход гарантирует, что из таблицы ВТ_УникальнаяНоменклатура ни одна строка не будет умножена в результате соединения, а в выборку попадут строго те комбинации номенклатуры и ее аналитик, которые еще не зарегистрированы на указанной кассе.

← На главную