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

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

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

Механика работы левого соединения: почему 9 превращается в 12?

Разберем фундаментальный принцип. Когда мы выполняем ЛЕВОЕ СОЕДИНЕНИЕ (LEFT JOIN) таблицы А с таблицей Б по некоторому условию, система ищет в таблице Б все записи, которые соответствуют условию. Важный момент: если для одной строки из таблицы А в таблице Б находится две, три или более подходящих строк, то строка из таблицы А будет продублирована в результате столько раз, сколько соответствий найдено справа.

Рассмотрим на примере из обсуждения. У автора было 9 строк в исходной таблице, а после соединения стало 12. Это означает, что для некоторых позиций (чеков или товаров) в присоединяемых таблицах (Штрихкоды, Кассиры или Купоны) нашлось более одного совпадения. Это не ошибка платформы 1С, а стандартное поведение реляционных баз данных (SQL). Левое соединение — это не "приклеивание" одного значения, а создание комбинаций всех подходящих пар.

Анализируем "слабые места" в представленном коде

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

  1. Соединение со справочником Кассиры:
    
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Кассиры КАК Кассиры
    ПО ТЗ.НомерКассира = Кассиры.Код
    

    Здесь кроется потенциальная проблема. В 1С уникальность поля Код гарантируется только в пределах справочника (или владельца/серии кодов). Если в справочнике Кассиры есть два элемента с одинаковым кодом (например, один из них помечен на удаление или был перенесен из другой базы с нарушением уникальности), то на каждую строку продажи система подтянет обе записи кассиров. В результате количество строк увеличится.

  2. Соединение с регистром сведений Штрихкоды:
    
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Штрихкоды КАК Штрихкоды
    ПО ТЗ.ШК = Штрихкоды.Штрихкод
    

    Это самое частое место возникновения дублей в торговых решениях. Один и тот же штрихкод может быть назначен разным единицам измерения одного и того же товара (например, "шт" и "упак"), разным характеристикам или даже разным товарам (пересортица в данных). Если в регистре Штрихкоды для одного значения ТЗ.ШК есть две записи, строка продажи раздвоится.

  3. Соединение с временной таблицей Купоны:
    
    ЛЕВОЕ СОЕДИНЕНИЕ Купоны КАК Купоны
    ПО Сшк.Позиция = Купоны.НомерПозиции
        И (Сшк.КодТовара ПОДОБНО Купоны.КодТовара)
        И Сшк.Магазин = Купоны.Магазин
        И Сшк.Касса = Купоны.Касса
        И (Сшк.Смена ПОДОБНО Купоны.Смена)
    

    Здесь мы видим использование оператора ПОДОБНО. Выясним причину опасности: если в таблице Купоны для одной и той же позиции чека зафиксировано несколько применений маркетинговых акций или карт (например, основной бонус и дополнительный купон), то соединение по позиции и коду товара вернет несколько строк. Оператор ПОДОБНО также может захватить лишние данные, если в кодах товаров есть схожие префиксы.

Как гарантировать уникальность строк при соединении?

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

Шаг 1: Предварительная группировка правой таблицы

Если нам нужно просто получить какой-то один параметр из правой таблицы (например, номер карты), а записей там может быть несколько, следует сначала подготовить эту таблицу. Сгруппируем её так, чтобы на один набор ключевых полей приходилось одно значение.

Посмотрим на пример трансформации таблицы Купоны:


ВЫБРАТЬ
    ТЗ1.Магазин,
    ТЗ1.Касса,
    ТЗ1.Смена,
    ТЗ1.НомерПозиции,
    МАКСИМУМ(ТЗ1.НомерКарты) КАК НомерКарты
ПОМЕСТИТЬ Купоны_Сгруппированные
ИЗ
    &ТЗ1 КАК ТЗ1
СГРУППИРОВАТЬ ПО
    ТЗ1.Магазин,
    ТЗ1.Касса,
    ТЗ1.Смена,
    ТЗ1.НомерПозиции
;

После такой подготовки соединение с Купоны_Сгруппированные никогда не приведет к дублям, так как комбинация "Магазин-Касса-Смена-Позиция" станет уникальной.

Шаг 2: Использование среза последних для регистров

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

Шаг 3: Проверка данных справочников

Если дубли возникают из-за справочника Кассиры, проанализируем ситуацию: возможно, стоит добавить условие ГДЕ НЕ Кассиры.ПометкаУдаления или использовать соединение по ссылке (УИД), а не по строковому коду. В 1С соединение по ссылке (Ref) всегда надежнее, чем по коду или наименованию.

Оптимизация производительности: индексация временных таблиц

Разберем еще один критический аспект, заметный в коде автора. При работе с временными таблицами (ТЗ, Купоны, Сшк) крайне важно использовать индексацию. Без индексов каждое соединение превращается в полный перебор строк, что при росте объема данных приведет к зависанию системы.

Рекомендуется добавлять инструкцию ИНДЕКСИРОВАТЬ ПО для полей, участвующих в связях:


|ПОМЕСТИТЬ ТЗ
|ИЗ
|    &ТЗ КАК ТЗ
|ИНДЕКСИРОВАТЬ ПО
|    Позиция,
|    Магазин,
|    Касса
|;

Подведем итоги

Выясним главные правила безопасного использования ЛЕВОЕ СОЕДИНЕНИЕ:

Помните, что запрос в 1С — это мощный инструмент, который требует дисциплины в работе с данными. Увеличение количества строк — это сигнал о том, что ваши данные "справа" менее структурированы, чем вы ожидали.

← На главную