При разработке отчетов в 1С (например, в конфигурации Управление торговлей 11) программисты часто сталкиваются с ситуацией, когда ЛЕВОЕ СОЕДИНЕНИЕ ведет себя как ВНУТРЕННЕЕ. Рассмотрим типичный сценарий: нам нужно вывести все проведенные Заказы клиентов и пристыковать к ним данные по Реализациям товаров и услуг. Если реализации по заказу еще нет, строка все равно должна остаться в результате с пустыми полями реализации. Однако на практике такие строки часто «вылетают» из итоговой таблицы.
В этой статье мы разберем по шагам, почему так происходит, проанализируем ошибки в написании условий и рассмотрим наиболее архитектурно правильный способ решения проблемы через временные таблицы.
Первоначальная причина проблемы кроется в особенностях работы SQL-запросов с трехзначной логикой (Истина, Ложь, Unknown/NULL). Проанализируем типичный запрос, который приводит к ошибке:
ВЫБРАТЬ
ЗаказКлиента.Ссылка,
РеализацияТоваровУслуг.Ссылка КАК Реализация
ИЗ
Документ.ЗаказКлиента КАК ЗаказКлиента
ЛЕВОЕ СОЕДИНЕНИЕ
Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
ПО
ЗаказКлиента.Ссылка = РеализацияТоваровУслуг.ЗаказКлиента
ГДЕ
ЗаказКлиента.Проведен = ИСТИНА
И РеализацияТоваровУслуг.Проведен = ИСТИНА
Выясним причину некорректной работы этого кода. Когда мы используем ЛЕВОЕ СОЕДИНЕНИЕ, для заказов без реализации в поле РеализацияТоваровУслуг.Проведен попадает значение NULL. В секции ГДЕ условие NULL = ИСТИНА дает результат «Ложь». В итоге вся строка, которая должна была быть в отчете, отсекается фильтром.
Важный момент: любое условие на «правую» (присоединяемую) таблицу в секции ГДЕ фактически превращает соединение во внутреннее, если только вы явно не обрабатываете NULL.
Самый простой способ исправить ситуацию — перенести фильтры, относящиеся к присоединяемой таблице, непосредственно в условие связи ПО. Рассмотрим, как это изменит логику:
ВЫБРАТЬ
ЗаказКлиента.Ссылка,
РеализацияТоваровУслуг.Ссылка КАК Реализация
ИЗ
Документ.ЗаказКлиента КАК ЗаказКлиента
ЛЕВОЕ СОЕДИНЕНИЕ
Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
ПО
ЗаказКлиента.Ссылка = РеализацияТоваровУслуг.ЗаказКлиента
И РеализацияТоваровУслуг.Проведен = ИСТИНА
ГДЕ
ЗаказКлиента.Проведен = ИСТИНА
В этом случае платформа сначала отфильтрует реализации по признаку проведенности, а уже затем попытается «приклеить» их к заказам. Если подходящей реализации нет, условие связи не сработает, но строка основного заказа останется в результате.
Если по каким-то причинам условие должно остаться в секции ГДЕ, необходимо добавить проверку на отсутствие записи в правой таблице:
ГДЕ
ЗаказКлиента.Проведен = ИСТИНА
И (РеализацияТоваровУслуг.Проведен = ИСТИНА ИЛИ РеализацияТоваровУслуг.Ссылка ЕСТЬ NULL)
Этот метод менее элегантен, чем первый, но он наглядно показывает системе, что нас устраивают строки, по которым реализации отсутствуют.
Для сложных отчетов, где участвуют большие таблицы (десятки тысяч документов), лучшей практикой считается использование Временных Таблиц (ВТ). Это позволяет избежать проблем с планом запроса SQL и делает код читаемым. Разберем этот алгоритм:
Посмотрим на пример реализации такого подхода:
// 1. Помещаем реализации во временную таблицу
ВЫБРАТЬ
Реализация.Ссылка КАК Ссылка,
Реализация.ЗаказКлиента КАК ЗаказКлиента,
Реализация.СуммаДокумента КАК Сумма
ПОМЕСТИТЬ ВТ_Реализации
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
Реализация.Проведен = ИСТИНА;
// 2. Основной запрос
ВЫБРАТЬ
Заказ.Ссылка КАК ЗаказСсылка,
Заказ.Дата КАК ДатаЗК,
Состояния.Состояние КАК ТекущееСостояние,
ВТ_Реализации.Ссылка КАК РеализацияСсылка,
ВТ_Реализации.Сумма КАК СуммаРеализации
ИЗ
Документ.ЗаказКлиента КАК Заказ
ЛЕВОЕ СОЕДИНЕНИЕ
РегистрСведений.СостоянияЗаказовКлиентов КАК Состояния
ПО
Заказ.Ссылка = Состояния.Заказ
ЛЕВОЕ СОЕДИНЕНИЕ
ВТ_Реализации КАК ВТ_Реализации
ПО
Заказ.Ссылка = ВТ_Реализации.ЗаказКлиента
ГДЕ
Заказ.Проведен = ИСТИНА
И Состояния.Состояние <> ЗНАЧЕНИЕ(Перечисление.СостоянияЗаказовКлиентов.Закрыт)
Проанализируем еще один аспект — «разыменование» ссылочных полей через точку. Рассмотрим плохой пример:
РеализацияТоваровУслуг.ЗаказКлиента.Ссылка
Почему это плохо: в языке запросов 1С каждое обращение через точку от ссылочного поля — это неявное ЛЕВОЕ СОЕДИНЕНИЕ на уровне SQL. Если вы уже соединили таблицу заказов и реализации, всегда обращайтесь к полям напрямую из источника. Использование точек внутри условий связи или выборки может привести к многократному увеличению количества соединений в итоговом SQL-запросе, что критически замедлит отчет.
При работе в УТ 11 важно помнить, что регистр СостоянияЗаказовКлиентов заполняется программно. Если вы делаете соединение с ним, помните:
Состояние <> Закрыт в секции ГДЕ, заказы, которых нет в регистре (новые или старые архивные), исчезнут из отчета.Поэтому в данном случае условие по состоянию также критически важно выносить в секцию ПО при ЛЕВОМ СОЕДИНЕНИИ.
Итог: Если ваш запрос «теряет» данные, первым делом проверьте секцию ГДЕ. Все условия на поля правой таблицы должны быть либо в блоке ПО, либо обрабатываться через ЕСТЬ NULL. А для высокой производительности всегда используйте временные таблицы с индексами по полям связи.