Как правильно использовать соединения в запросах 1С, чтобы не терять строки с NULL

Программист 1С v8.3 (Управляемые формы) 1С:ERP Управление предприятием Управленческий учет Промышленность, строительство и АПК
← На главную

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

Проблема: Превращение внешнего соединения во внутреннее

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

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


ВЫБРАТЬ
    ЗаданиеНаПроизводствоНовоеШвейныеОперации.ШвейнаяОперация КАК ТекущаяШвейнаяОперация,
    ПаспортИзделияОперацииПоИзделию.ВидРабот КАК ПаспортШвейнаяОперация
ИЗ
    Справочник.ЗаданиеНаПроизводствоНовое.ШвейныеОперации КАК ЗаданиеНаПроизводствоНовоеШвейныеОперации
        ПОЛНОЕ СОЕДИНЕНИЕ Документ.ПаспортИзделия.ОперацииПоИзделию КАК ПаспортИзделияОперацииПоИзделию
        ПО ЗаданиеНаПроизводствоНовоеШвейныеОперации.ШвейнаяОперация = ПаспортИзделияОперацииПоИзделию.ВидРабот
ГДЕ
    ЗаданиеНаПроизводствоНовоеШвейныеОперации.Ссылка = &Задание
    И ПаспортИзделияОперацииПоИзделию.Ссылка = &Паспорт

Проанализируем ситуацию: почему этот запрос вернет только те строки, которые есть в обеих таблицах? Проблема кроется в секции ГДЕ. Когда платформа (и СУБД под ней) выполняет соединение, она создает временный набор данных, включающий NULL там, где нет совпадений. Но затем применяется фильтр ГДЕ ПаспортИзделияОперацииПоИзделию.Ссылка = &Паспорт. Поскольку значение NULL не может быть равно значению параметра &Паспорт, все строки с пустыми значениями из правой таблицы просто отсекаются. В итоге ПОЛНОЕ СОЕДИНЕНИЕ или ЛЕВОЕ СОЕДИНЕНИЕ фактически превращается во ВНУТРЕННЕЕ СОЕДИНЕНИЕ.

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

Самый простой и правильный способ решить эту задачу — вынести фильтры по конкретным документам или справочникам непосредственно в условие соединения. В этом случае СУБД сначала отфильтрует данные по ссылкам, а уже потом выполнит соединение, сохранив строки с NULL.

Исправленный вариант запроса будет выглядеть так:


ВЫБРАТЬ
    ЗаданиеНаПроизводствоНовоеШвейныеОперации.ШвейнаяОперация КАК ТекущаяШвейнаяОперация,
    ПаспортИзделияОперацииПоИзделию.ВидРабот КАК ПаспортШвейнаяОперация
ИЗ
    Справочник.ЗаданиеНаПроизводствоНовое.ШвейныеОперации КАК ЗаданиеНаПроизводствоНовоеШвейныеОперации
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПаспортИзделия.ОперацииПоИзделию КАК ПаспортИзделияОперацииПоИзделию
        ПО ЗаданиеНаПроизводствоНовоеШвейныеОперации.ШвейнаяОперация = ПаспортИзделияОперацииПоИзделию.ВидРабот
        И ПаспортИзделияОперацииПоИзделию.Ссылка = &Паспорт
ГДЕ
    ЗаданиеНаПроизводствоНовоеШвейныеОперации.Ссылка = &Задание

Обратите внимание: если нам нужны все строки из "Задания", мы используем ЛЕВОЕ СОЕДИНЕНИЕ. Условие по ссылке паспорта теперь находится внутри оператора ПО. Это гарантирует, что если в конкретном паспорте не найдется нужной операции, строка из задания все равно останется в выборке, а поля паспорта заполнятся значением NULL.

Решение №2: Использование временных таблиц (Best Practice)

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

Разберем по шагам, как это сделать:

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

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


// Шаг 1: Подготовка операций задания
ВЫБРАТЬ
    ЗаданиеОперации.ШвейнаяОперация КАК Операция
ПОМЕСТИТЬ ВТ_Задание
ИЗ
    Справочник.ЗаданиеНаПроизводствоНовое.ШвейныеОперации КАК ЗаданиеОперации
ГДЕ
    ЗаданиеОперации.Ссылка = &Задание;

// Шаг 2: Подготовка операций паспорта
ВЫБРАТЬ
    ПаспортОперации.ВидРабот КАК Операция
ПОМЕСТИТЬ ВТ_Паспорт
ИЗ
    Документ.ПаспортИзделия.ОперацииПоИзделию КАК ПаспортОперации
ГДЕ
    ПаспортОперации.Ссылка = &Паспорт;

// Шаг 3: Финальное соединение
ВЫБРАТЬ
    ВТ_Задание.Операция КАК ТекущаяШвейнаяОперация,
    ВТ_Паспорт.Операция КАК ПаспортШвейнаяОперация
ИЗ
    ВТ_Задание КАК ВТ_Задание
        ПОЛНОЕ СОЕДИНЕНИЕ ВТ_Паспорт КАК ВТ_Паспорт
        ПО ВТ_Задание.Операция = ВТ_Паспорт.Операция

Такой метод не только решает проблему с NULL, но и значительно упрощает чтение кода, а также позволяет СУБД построить оптимальный план запроса.

Важные нюансы работы с соединениями

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

Также не забывайте про обработку NULL в итоговой выборке. Чтобы в отчете или коде не возникало ошибок при обращении к пустым значениям, рекомендуется использовать функцию ЕСТЬNULL. Проанализируем пример:


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

Использование ЕСТЬNULL позволяет заменить пустоты на типизированные данные (пустую ссылку, ноль или строку), что делает дальнейшую работу с результатом запроса более стабильной.

Резюме по оптимизации

Для эффективной работы запросов придерживайтесь следующих правил:

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

← На главную