Как исключить пустые ссылки и заменить их на Неопределено или NULL в запросе 1С?

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

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

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

Анализ проблемы: почему пустая ячейка не делает запрос пустым?

Давайте проанализируем исходную ситуацию. У нас есть запрос к табличной части «Товары» документа «Поступление товаров и услуг»:


ВЫБРАТЬ РАЗРЕШЕННЫЕ РАЗЛИЧНЫЕ
    ПоступлениеТоваровУслугТовары.ЗаказПоставщику КАК ЗаказПоставщику
ИЗ
    Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТоваровУслугТовары
ГДЕ
    ПоступлениеТоваровУслугТовары.Ссылка = &СсылкаНаОбъект

Если в документе-источнике реквизит ЗаказПоставщику не заполнен, СУБД все равно возвращает запись. Дело в том, что в базе данных 1С пустая ссылка — это не пустота (не NULL) и не отсутствие типа. Это физическая запись, состоящая из уникального идентификатора (UUID/GUID), заполненного нулями (00000000-0000-0000-0000-000000000000). С точки зрения базы данных, это валидное значение, поэтому строка попадает в выборку.

Решение 1. Фильтрация пустых ссылок через условие ГДЕ

Самый простой и производительный способ избавиться от таких строк — добавить в секцию ГДЕ проверку на равенство пустой ссылке конкретного типа документа. Рассмотрим, как это выглядит на практике:


ВЫБРАТЬ РАЗРЕШЕННЫЕ РАЗЛИЧНЫЕ
    ПоступлениеТоваровУслугТовары.ЗаказПоставщику КАК ЗаказПоставщику
ИЗ
    Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТоваровУслугТовары
ГДЕ
    ПоступлениеТоваровУслугТовары.Ссылка = &СсылкаНаОбъект
    И ПоступлениеТоваровУслугТовары.ЗаказПоставщику <> ЗНАЧЕНИЕ(Документ.ЗаказПоставщику.ПустаяСсылка)

Использование конструкции ЗНАЧЕНИЕ(...) крайне важно. Это предопределенная функция языка запросов 1С, которая вычисляется еще на этапе компиляции запроса самой платформой. В СУБД передается уже готовый двоичный идентификатор пустой ссылки, что позволяет СУБД выполнить запрос максимально быстро, используя существующие индексы. Для интерактивной проверки и отладки подобных запросов можно использовать консоль запросов для управляемых форм.

Мифы и реальность: разыменование ссылок и неявные левые соединения

В процессе обсуждения подобных задач среди разработчиков часто возникает спор о «неявных левых соединениях» (Implicit Left Joins). Давайте выясним истину и разберем этот механизм детально.

Существует ошибочное мнение, что любое обращение к ссылочному реквизиту (например, ПоступлениеТоваровУслугТовары.ЗаказПоставщику) заставляет платформу выполнять неявное левое соединение с таблицей этого документа. Это не так.

Рассмотрим два сценария обращения к данным:

  1. Прямое обращение к ссылке: Когда мы пишем ПоступлениеТоваровУслугТовары.ЗаказПоставщику, платформа просто считывает шестнадцатеричный идентификатор (UUID) из физического поля таблицы табличной части. Никаких обращений к таблице «Заказ поставщику» не происходит. Запрос выполняется быстро, в рамках одной таблицы.
  2. Разыменование ссылки (обращение через точку): Если бы мы написали ПоступлениеТоваровУслугТовары.ЗаказПоставщику.Дата, СУБД была бы вынуждена выполнить операцию LEFT JOIN с таблицей документов «Заказ поставщику», чтобы получить дату конкретного заказа. Вот это и называется неявным левым соединением, которое при неаккуратном использовании может существенно замедлить работу базы данных.

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

Почему внутреннее соединение для фильтрации — плохой совет?

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


// НЕОПТИМАЛЬНЫЙ ВАРИАНТ!
ВЫБРАТЬ РАЗРЕШЕННЫЕ РАЗЛИЧНЫЕ
    ПоступлениеТоваровУслугТовары.ЗаказПоставщику КАК ЗаказПоставщику
ИЗ
    Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТоваровУслугТовары
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ЗаказПоставщику КАК ЗаказПоставщику
        ПО ПоступлениеТоваровУслугТовары.ЗаказПоставщику = ЗаказПоставщику.Ссылка
ГДЕ
    ПоступлениеТоваровУслугТовары.Ссылка = &СсылкаНаОбъект

При таком подходе СУБД вынуждена сканировать индекс таблицы «Заказы поставщикам» и сопоставлять данные. Если таблица заказов содержит сотни тысяч записей, это приведет к избыточному потреблению оперативной памяти и нагрузке на дисковую подсистему сервера. Использование простого условия ГДЕ ... <> ЗНАЧЕНИЕ(...) работает локально в рамках одной таблицы и всегда будет в разы быстрее.

Как заменить пустую ссылку на НЕОПРЕДЕЛЕНО или NULL в выборке

Иногда бизнес-логика требует не просто отфильтровать строки, а вернуть их, заменив пустые ссылки на полноценные маркеры отсутствия данных — Неопределено или NULL. Это особенно полезно при последующем объединении таблиц или выводе данных в отчеты СКД.

Для решения этой задачи мы воспользуемся условной конструкцией ВЫБОР... КОГДА... ТОГДА... ИНАЧЕ... КОНЕЦ.

Вариант 1. Замена на НЕОПРЕДЕЛЕНО

Этот вариант чаще всего применяется для реквизитов, которые в дальнейшем будут обрабатываться в коде на встроенном языке 1С, либо для полей составного типа данных, где тип еще не определен.


ВЫБРАТЬ
    ВЫБОР
        КОГДА ПоступлениеТоваровУслугТовары.ЗаказПоставщику = ЗНАЧЕНИЕ(Документ.ЗаказПоставщику.ПустаяСсылка)
            ТОГДА НЕОПРЕДЕЛЕНО
        ИНАЧЕ ПоступлениеТоваровУслугТовары.ЗаказПоставщику
    КОНЕЦ КАК ЗаказПоставщику
ИЗ
    Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТоваровУслугТовары
ГДЕ
    ПоступлениеТоваровУслугТовары.Ссылка = &СсылкаНаОбъект

Вариант 2. Замена на NULL

Этот вариант незаменим, если результат запроса планируется использовать во временных таблицах для последующих левых соединений (связей), так как СУБД оперирует понятием NULL на уровне ядра.


ВЫБРАТЬ
    ВЫБОР
        КОГДА ПоступлениеТоваровУслугТовары.ЗаказПоставщику = ЗНАЧЕНИЕ(Документ.ЗаказПоставщику.ПустаяСсылка)
            ТОГДА NULL
        ИНАЧЕ ПоступлениеТоваровУслугТовары.ЗаказПоставщику
    КОНЕЦ КАК ЗаказПоставщику
ИЗ
    Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТоваровУслугТовары
ГДЕ
    ПоступлениеТоваровУслугТовары.Ссылка = &СсылкаНаОбъект

Особенности работы с составными типами данных

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

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

Подход А. Ограничение типа через ВЫРАЗИТЬ (Оптимальный)

Если в данном контексте нас интересует только конкретный тип документа из всех возможных в составе реквизита, мы принудительно ограничиваем его. Это исключает лишние проверки в СУБД:


ГДЕ
    ВЫРАЗИТЬ(ПоступлениеТоваровУслугТовары.ЗаказПоставщику КАК Документ.ЗаказПоставщику) <> ЗНАЧЕНИЕ(Документ.ЗаказПоставщику.ПустаяСсылка)

Подход Б. Проверка через ЕСТЬ NULL с разыменованием (Универсальный)

Если поле может содержать пустую ссылку любого типа или значение Неопределено, мы можем использовать обращение к предопределенному свойству Ссылка. Если ссылка пустая или «битая» (указывает на несуществующий в базе объект), обращение к свойству вернет базу NULL:


ГДЕ
    НЕ ПоступлениеТоваровУслугТовары.ЗаказПоставщику.Ссылка ЕСТЬ NULL

Важное предупреждение: Использование конструкции .Ссылка ЕСТЬ NULL для составного типа приводит к тому, что платформа 1С генерирует SQL-запрос с неявным левым соединением (LEFT JOIN) со всеми таблицами, входящими в этот составной тип. Если типов в составе реквизита очень много (например, более 10-15), это может критически замедлить выполнение запроса. Поэтому всегда отдавайте предпочтение конструкции ВЫРАЗИТЬ.

← На главную