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

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

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

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

Разбор механизма «исчезновения» строк (Ловушка секции ГДЕ)

Первоначальная причина проблемы кроется в особенностях работы SQL-запросов с трехзначной логикой (Истина, Ложь, Unknown/NULL). Проанализируем типичный запрос, который приводит к ошибке:


ВЫБРАТЬ
    ЗаказКлиента.Ссылка,
    РеализацияТоваровУслуг.Ссылка КАК Реализация
ИЗ
    Документ.ЗаказКлиента КАК ЗаказКлиента
ЛЕВОЕ СОЕДИНЕНИЕ 
    Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
ПО 
    ЗаказКлиента.Ссылка = РеализацияТоваровУслуг.ЗаказКлиента
ГДЕ
    ЗаказКлиента.Проведен = ИСТИНА
    И РеализацияТоваровУслуг.Проведен = ИСТИНА

Выясним причину некорректной работы этого кода. Когда мы используем ЛЕВОЕ СОЕДИНЕНИЕ, для заказов без реализации в поле РеализацияТоваровУслуг.Проведен попадает значение NULL. В секции ГДЕ условие NULL = ИСТИНА дает результат «Ложь». В итоге вся строка, которая должна была быть в отчете, отсекается фильтром.

Важный момент: любое условие на «правую» (присоединяемую) таблицу в секции ГДЕ фактически превращает соединение во внутреннее, если только вы явно не обрабатываете NULL.

Решение №1: Перенос условий в секцию связи ПО

Самый простой способ исправить ситуацию — перенести фильтры, относящиеся к присоединяемой таблице, непосредственно в условие связи ПО. Рассмотрим, как это изменит логику:


ВЫБРАТЬ
    ЗаказКлиента.Ссылка,
    РеализацияТоваровУслуг.Ссылка КАК Реализация
ИЗ
    Документ.ЗаказКлиента КАК ЗаказКлиента
ЛЕВОЕ СОЕДИНЕНИЕ 
    Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
ПО 
    ЗаказКлиента.Ссылка = РеализацияТоваровУслуг.ЗаказКлиента
    И РеализацияТоваровУслуг.Проведен = ИСТИНА
ГДЕ
    ЗаказКлиента.Проведен = ИСТИНА

В этом случае платформа сначала отфильтрует реализации по признаку проведенности, а уже затем попытается «приклеить» их к заказам. Если подходящей реализации нет, условие связи не сработает, но строка основного заказа останется в результате.

Решение №2: Использование конструкции ЕСТЬ NULL

Если по каким-то причинам условие должно остаться в секции ГДЕ, необходимо добавить проверку на отсутствие записи в правой таблице:


ГДЕ
    ЗаказКлиента.Проведен = ИСТИНА
    И (РеализацияТоваровУслуг.Проведен = ИСТИНА ИЛИ РеализацияТоваровУслуг.Ссылка ЕСТЬ NULL)

Этот метод менее элегантен, чем первый, но он наглядно показывает системе, что нас устраивают строки, по которым реализации отсутствуют.

Решение №3: Оптимальный вариант через Временные Таблицы (Best Practice)

Для сложных отчетов, где участвуют большие таблицы (десятки тысяч документов), лучшей практикой считается использование Временных Таблиц (ВТ). Это позволяет избежать проблем с планом запроса SQL и делает код читаемым. Разберем этот алгоритм:

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

Посмотрим на пример реализации такого подхода:


// 1. Помещаем реализации во временную таблицу
ВЫБРАТЬ
    Реализация.Ссылка КАК Ссылка,
    Реализация.ЗаказКлиента КАК ЗаказКлиента,
    Реализация.СуммаДокумента КАК Сумма
ПОМЕСТИТЬ ВТ_Реализации
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
    Реализация.Проведен = ИСТИНА;

// 2. Основной запрос
ВЫБРАТЬ
    Заказ.Ссылка КАК ЗаказСсылка,
    Заказ.Дата КАК ДатаЗК,
    Состояния.Состояние КАК ТекущееСостояние,
    ВТ_Реализации.Ссылка КАК РеализацияСсылка,
    ВТ_Реализации.Сумма КАК СуммаРеализации
ИЗ
    Документ.ЗаказКлиента КАК Заказ
ЛЕВОЕ СОЕДИНЕНИЕ 
    РегистрСведений.СостоянияЗаказовКлиентов КАК Состояния
ПО 
    Заказ.Ссылка = Состояния.Заказ
ЛЕВОЕ СОЕДИНЕНИЕ 
    ВТ_Реализации КАК ВТ_Реализации
ПО 
    Заказ.Ссылка = ВТ_Реализации.ЗаказКлиента
ГДЕ
    Заказ.Проведен = ИСТИНА
    И Состояния.Состояние <> ЗНАЧЕНИЕ(Перечисление.СостоянияЗаказовКлиентов.Закрыт)

Типичные ошибки производительности при соединениях

Проанализируем еще один аспект — «разыменование» ссылочных полей через точку. Рассмотрим плохой пример:

РеализацияТоваровУслуг.ЗаказКлиента.Ссылка

Почему это плохо: в языке запросов 1С каждое обращение через точку от ссылочного поля — это неявное ЛЕВОЕ СОЕДИНЕНИЕ на уровне SQL. Если вы уже соединили таблицу заказов и реализации, всегда обращайтесь к полям напрямую из источника. Использование точек внутри условий связи или выборки может привести к многократному увеличению количества соединений в итоговом SQL-запросе, что критически замедлит отчет.

Особенности регистра «Состояния заказов»

При работе в УТ 11 важно помнить, что регистр СостоянияЗаказовКлиентов заполняется программно. Если вы делаете соединение с ним, помните:

Поэтому в данном случае условие по состоянию также критически важно выносить в секцию ПО при ЛЕВОМ СОЕДИНЕНИИ.

Итог: Если ваш запрос «теряет» данные, первым делом проверьте секцию ГДЕ. Все условия на поля правой таблицы должны быть либо в блоке ПО, либо обрабатываться через ЕСТЬ NULL. А для высокой производительности всегда используйте временные таблицы с индексами по полям связи.

← На главную