Почему запрос в 1С возвращает пустой результат, если одна из временных таблиц пустая?

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

При разработке сложных пакетных запросов в платформе 1С:Предприятие часто возникает задача объединения данных из нескольких временных таблиц. Типичный сценарий: в первой временной таблице сформирован базовый список сотрудников, а во второй — список их замещений или отсутствий. Однако если вторая таблица оказывается пустой, итоговый запрос может неожиданно вернуть совершенно пустой результат. Разберем по шагам, почему так происходит и как правильно построить запрос.

Причина проблемы: декартово произведение при перечислении таблиц через запятую

Проанализируем исходный фрагмент запроса, вызывающий ошибку:


ВЫБРАТЬ
    ВЫБОР
        КОГДА ВТОтветственныеЛица.ФИО = ВТЗамещаемые.ЗамещаемоеЛицо
            ТОГДА ВТЗамещаемые.ЗамещающееЛицо
        ИНАЧЕ ВТОтветственныеЛица.ФИО
    КОНЕЦ КАК ФИО,
    ВТОтветственныеЛица.ОтветственноеЛицо,
    ВТОтветственныеЛица.ГлавнаяВиза,
    ВЫБОР
        КОГДА ВТОтветственныеЛица.ФИО = ВТЗамещаемые.ЗамещаемоеЛицо
            ТОГДА ВТЗамещаемые.ДолжностьЗамещающего
        ИНАЧЕ ВТОтветственныеЛица.Должность
    КОНЕЦ КАК Должность
ИЗ
    ВТОтветственныеЛица КАК ВТОтветственныеЛица,
    ВТЗамещаемые КАК ВТЗамещаемые

Выясним причину такого поведения. Запись таблиц через запятую в секции ИЗ (ИЗ ВТОтветственныеЛица, ВТЗамещаемые) означает полное декартово произведение (CROSS JOIN). В этом случае СУБД пытается сопоставить каждую строку первой таблицы с каждой строкой второй таблицы.

Если в таблице ВТОтветственныеЛица находится 10 строк, а в таблице ВТЗамещаемые — 0 строк, результатом умножения 10 на 0 будет ровно 0 строк. Конструкции ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ в блоке ВЫБРАТЬ даже не начинают вычисляться, поскольку на этапе формирования набора данных в секции ИЗ результирующая выборка уже пуста.

Решение: правильное использование соединения таблиц

Для сохранения всех записей основной таблицы необходимо использовать явные соединения. Разберем варианты соединения и выберем оптимальный:

  1. ЛЕВОЕ СОЕДИНЕНИЕ (LEFT JOIN) — идеальный выбор для данной задачи. В результат гарантированно попадут все строки из левой таблицы (ВТОтветственныеЛица). Если во второй таблице (ВТЗамещаемые) нет подходящей записи, поля второй таблицы просто примут значение NULL, но сами строки не пропадут.
  2. ПОЛНОЕ СОЕДИНЕНИЕ (FULL JOIN) — не рекомендуется в таких сценариях. Если во второй таблице появятся записи по сотрудникам, которых нет в основном списке, полное соединение добавит лишние пустые строки, исказив выборку.

Оптимизация: замена громоздких условий на функцию ЕСТЬNULL

При использовании ЛЕВОГО СОЕДИНЕНИЯ условие связи выносится в секцию ПО. В результате отпадает необходимость писать сложные конструкции ВЫБОР КОГДА для каждого поля. Достаточно применить функцию ЕСТЬNULL, которая при отсутствии замещающего лица автоматически подставит данные основного сотрудника.

Посмотрим на правильный и компактный вариант итогового запроса:


ВЫБРАТЬ
    ЕСТЬNULL(ВТЗамещаемые.ЗамещающееЛицо, ВТОтветственныеЛица.ФизическоеЛицо) КАК ФизическоеЛицо,
    ВТОтветственныеЛица.ОтветственноеЛицо КАК ОтветственноеЛицо,
    ВТОтветственныеЛица.ГлавнаяВиза КАК ГлавнаяВиза,
    ЕСТЬNULL(ВТЗамещаемые.ДолжностьЗамещающего, ВТОтветственныеЛица.Должность) КАК Должность,
    ЕСТЬNULL(ВТЗамещаемые.ПодразделениеОрганизации, ВТОтветственныеЛица.ПодразделениеОрганизации) КАК ПодразделениеОрганизации
ИЗ
    ВТОтветственныеЛица КАК ВТОтветственныеЛица
        ЛЕВОЕ СОЕДИНЕНИЕ ВТЗамещаемые КАК ВТЗамещаемые
        ПО ВТОтветственныеЛица.ФизическоеЛицо = ВТЗамещаемые.ЗамещаемоеЛицо

Важные рекомендации при работе с временными таблицами

Рассмотрим подробнее тонкости, которые позволят избежать скрытых ошибок и проблем с производительностью:

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