Реквизит составного типа: как правильно проверить на пустое значение в запросе?

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

При работе с регистрами или документами в 1С мы часто сталкиваемся с реквизитами составного типа данных. Особенность таких полей заключается в том, что понятие «пустое значение» для них неоднозначно. В этой статье мы подробно разберем, почему обычная проверка на NULL не работает, и рассмотрим эффективные методы решения этой задачи в языке запросов.

Поймем разницу: Неопределено, Пустая ссылка и NULL

Прежде чем переходить к написанию кода, давайте проанализируем ситуацию с точки зрения хранения данных. Для составного типа в 1С существует три принципиально разных состояния «пустоты»:

  1. Неопределено (Undefined): Это состояние, когда в реквизите не выбран даже тип значения. Это «абсолютная» пустота для составного типа.
  2. Пустая ссылка (EmptyRef): Это состояние, когда тип значения уже выбран (например, «Документ.Отпуск»), но конкретный объект не указан. В базе данных это выглядит как ссылка, состоящая из нулей, но имеющая конкретный тип.
  3. NULL: Это отсутствие значения как такового в результате соединения таблиц. В реальном реквизите физической таблицы (если это не результат левого соединения) значения NULL быть не может.

Рассмотрим ситуацию, когда автор ветки пытается использовать ЕСТЬ NULL. Как справедливо заметили коллеги, это не сработает для проверки самого поля, так как 1С хранит составной тип в СУБД в виде нескольких колонок (тип и идентификатор). Для прямой проверки поля в основной таблице нужно использовать другие механизмы.

Способ 1. Проверка на Неопределено

Самый простой и быстрый способ проверить, что в поле вообще ничего не выбрано (даже тип), — это сравнение с литералом НЕОПРЕДЕЛЕНО. Проанализируем пример кода:


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

Этот метод работает молниеносно, но у него есть существенный минус: он не найдет пустые ссылки конкретных типов. Если пользователь выбрал тип документа, но очистил само поле, там останется пустая ссылка, которая не равна НЕОПРЕДЕЛЕНО.

Способ 2. Использование функции УНИКАЛЬНЫЙИДЕНТИФИКАТОР (для платформы 8.3.11+)

Одним из самых современных и изящных способов решения проблемы является использование функции УНИКАЛЬНЫЙИДЕНТИФИКАТОР прямо в тексте запроса. Давайте разберем, как это работает. Если значение реквизита равно Неопределено, функция вернет NULL. Если же там пустая ссылка любого типа, функция вернет пустой GUID.

Посмотрим на реализацию этого метода:


// Сначала подготовим пустой идентификатор в коде 1С
ПустойИдентификатор = Новый УникальныйИдентификатор("00000000-0000-0000-0000-000000000000");
Запрос.УстановитьПараметр("ПустойИдентификатор", ПустойИдентификатор);

// Текст запроса
Запрос.Текст = "
ВЫБРАТЬ
    Т.Ссылка
ИЗ
    Справочник.Договоры КАК Т
ГДЕ
    УНИКАЛЬНЫЙИДЕНТИФИКАТОР(Т.РеквизитСоставногоТипа) = &ПустойИдентификатор
    ИЛИ Т.РеквизитСоставногоТипа = НЕОПРЕДЕЛЕНО";

Важный нюанс: Этот метод доступен только в том случае, если ваша конфигурация работает в режиме совместимости не ниже 8.3.11. В типовых конфигурациях на старых версиях этот фокус может не пройти. Кроме того, использование функций в условии ГДЕ может замедлить запрос на очень больших объемах данных, так как СУБД будет сложнее использовать индексы.

Способ 3. Универсальный метод с массивом пустых значений

Если нам нужно решение, которое будет работать на любой версии платформы и максимально надежно, рассмотрим вариант с передачей массива всех возможных пустых значений в качестве параметра. Проанализируем этот алгоритм:

  1. Выясним, какие типы входят в составной реквизит.
  2. Для каждого типа создадим пустую ссылку.
  3. Добавим в массив значение Неопределено.
  4. Передадим этот массив в запрос.

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


МассивПустыхЗначений = Новый Массив;
МассивПустыхЗначений.Добавить(Неопределено);

// Получаем описание типов реквизита
ОписаниеТипов = Метаданные.РегистрыНакопления.МойРегистр.Реквизиты.РеквизитСоставногоТипа.Тип;

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

Запрос.УстановитьПараметр("МассивПустыхЗначений", МассивПустыхЗначений);
Запрос.Текст = "ВЫБРАТЬ * ИЗ РегистрНакопления.МойРегистр ГДЕ РеквизитСоставногоТипа В (&МассивПустыхЗначений)";

Этот метод считается наиболее производительным, так как он позволяет СУБД эффективно использовать индексы по полю реквизита.

Способ 4. Проверка через разыменование (Attribute.Ref IS NULL)

Существует еще один «хитрый» способ, который упоминали в обсуждении: ГДЕ Реквизит.Ссылка ЕСТЬ NULL. Давайте выясним, что происходит «под капотом» платформы при таком условии. Система будет вынуждена выполнить левое соединение со всеми таблицами, входящими в составной тип, чтобы проверить наличие ссылки.

Плюсы:

Минусы:

Рекомендации по проектированию

Проанализировав все способы, мы можем сделать вывод, что лучшая практика — это предотвращение проблемы на этапе записи данных. Профессиональные разработчики 1С рекомендуют при записи в регистры принудительно приводить любые пустые ссылки к значению Неопределено.

Если вы будете следовать правилу «в регистре пустая ссылка = Неопределено», то ваши запросы всегда будут выглядеть просто и работать максимально быстро:

ГДЕ Реквизит = НЕОПРЕДЕЛЕНО

Для этого в модуле набора записей регистра или перед записью объекта достаточно добавить небольшую проверку функцией ЗначениеЗаполнено(), которая корректно понимает и пустые ссылки, и Неопределено, и при необходимости очищать поле в Неопределено.

Итоги

Мы рассмотрели различные подходы к проверке составного типа. Выбор метода зависит от ваших условий:

Используйте эти знания, чтобы ваши запросы были не только правильными, но и оптимальными!

← На главную