При разработке конфигураций платформы 1С:Предприятие 8 программисты часто сталкиваются с необходимостью фильтрации данных, где определенные связи между объектами отсутствуют. Типичная ситуация — выборка документов, у которых не заполнено поле "Контрагент", или поиск номенклатуры без указания основного поставщика. В таких случаях критически важно понимать, как технически реализуется пустая ссылка на уровне базы данных и языка запросов.
Неправильная трактовка отсутствующего значения может привести к тому, что ваш отчет покажет лишние строки или, наоборот, скроет важные данные. В отличие от многих других систем управления базами данных, 1С имеет свою специфику обработки значений типа Ссылка, особенно когда речь заходит о сравнении с неопределенным значением. Давайте разберемся, как корректно сформулировать условие для получения записей с незаполненными полями.
Основной принцип заключается в использовании специального служебного значения NULL. Однако просто написать слово недостаточно — важно соблюдать синтаксис платформы и учитывать типизацию переменных в коде. Ниже мы подробно рассмотрим все нюансы, от базового синтаксиса до сложных случаев с объединением таблиц.
Синтаксис проверки на пустоту в языке запросов
В языке запросов 1С для проверки поля на отсутствие значения используется ключевое слово NULL. Это зарезервированное слово, которое указывает движку базы данных, что мы ищем записи, где конкретное поле не имеет ссылки на объект. Синтаксически это выглядит как обычное условие сравнения в блоке ГДЕ.
Рассмотрим простой пример запроса к регистру сведений или справочнику. Нам нужно получить список номенклатуры, для которой не указан основной поставщик. Поле "ОсновнойПоставщик" имеет тип Ссылка.Справочник.Контрагенты. Если это поле пустое, в базе данных оно хранится как NULL.
ВЫБРАТЬ
Номенклатура.Ссылка,
Номенклатура.Наименование
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.ОсновнойПоставщик ЕСТЬ NULL
Обратите внимание на использование оператора ЕСТЬ NULL (или IS NULL в зависимости от локализации и версии платформы, но стандартным для текстов запросов в конфигураторе является именно русскоязычный синтаксис или его эквивалент). Использование знака равенства, например ОсновнойПоставщик = NULL, является грубой ошибкой и никогда не вернет нужного результата, так как в реляционной алгебре NULL не равен самому себе.
⚠️ Внимание: Никогда не используйте оператор сравнения
=для проверки на пустоту ссылки. ВыражениеПоле = NULLвсегда возвращает ЛОЖЬ или НЕИЗВЕСТНО, но никогда ИСТИНУ. Используйте только конструкциюЕСТЬ NULLилиНЕ ЕСТЬ NULL.
Если вам необходимо найти записи, где ссылка заполнена, используется инверсия — оператор НЕ ЕСТЬ NULL. Это позволяет отфильтровать все объекты, у которых поле содержит корректный указатель на запись в базе данных. Такой подход гарантирует, что вы получите только те элементы, которые связаны с другими объектами системы.
При написании сложных условий комбинируйте проверку на NULL с другими фильтрами, используя скобки для группировки. Это предотвратит ошибки приоритета операторов И/ИЛИ.
Работа с параметрами запроса и пустыми ссылками
Часто логика приложения требует гибкости: пользователь может выбрать конкретного контрагента или оставить поле фильтра пустым, чтобы увидеть всех. В таких сценариях мы передаем параметр в запрос. Главная задача — корректно обработать ситуацию, когда в параметр передана пустая ссылка из интерфейса или кода.
В управляемых формах или обычном приложении переменная типа Ссылка может быть неопределенной. При передаче такой переменной в объект Запрос через метод УстановитьПараметр, платформа автоматически преобразует неопределенное значение в NULL базы данных. Это позволяет использовать единый текст запроса для обоих случаев.
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ... ГДЕ Номенклатура.Поставщик = &Поставщик";
Запрос.УстановитьПараметр("Поставщик", ВыбранныйПоставщик);
Однако здесь кроется подвох. Если вы используете прямое сравнение = &Параметр, и параметр равен NULL, запрос вернет пустой результат. Чтобы сделать фильтр опциональным (работать и при заполненном, и при пустом параметре), применяется специальная логика в условии ГДЕ.
Стандартный паттерн для опционального фильтра выглядит следующим образом: мы проверяем, равен ли параметр NULL. Если да — условие игнорируется (возвращает ИСТИНУ для всех строк). Если нет — выполняется сравнение по значению.
ГДЕ
(&Поставщик ЕСТЬ NULL ИЛИ Номенклатура.Поставщик = &Поставщик)
Такая конструкция обеспечивает универсальность. Если вы передали конкретную ссылку, сработает вторая часть условия после ИЛИ. Если передали пустоту, сработает первая часть, и фильтр фактически отключится. Это один из самых распространенных приемов в программировании 1С для динамических отчетов.
Особенности типизации и приведения типов
Платформа 1С:Предприятие строго следит за типами данных. Поле типа Ссылка может содержать NULL, но не может содержать число 0 или пустую строку. Понимание этого различия критично при написании запросов, особенно если вы работаете с полями составного типа или полями, которые могут менять свою природу в разных версиях конфигурации.
В некоторых редких случаях, при работе с таблицами значений, переданных в запрос, пустые ссылки могут вести себя иначе, чем в физических таблицах базы данных. Если вы формируете ТаблицуЗначений в коде и добавляете туда строки, не заполняя колонку типа Ссылка, при записи этой таблицы во временную таблицу запроса (ПОМЕСТИТЬ) пустые значения также станут NULL.
Важно помнить о составных типах. Если поле может быть Ссылка.Справочник или Число, то проверка на NULL остается валидной для отсутствия значения любого типа. Однако попытка сравнить такое поле с конкретной ссылкой, когда в нем лежит число, вызовет ошибку выполнения запроса.
⚠️ Внимание: Перед выполнением запроса с динамическими типами убедитесь, что структура метаданных не изменилась. Если вы добавили новый тип в поле состава, старый запрос может перестать работать или требовать явного приведения типов через функцию
ЕСТЬ.
Для явного контроля типов в условиях запроса можно использовать функцию ЕСТЬ. Она позволяет проверить, является ли значение конкретным типом, прежде чем выполнять сравнение. Это полезно при фильтрации таблиц с составными типами, где пустая ссылка — лишь один из вариантов "пустоты", но не единственный.
ГДЕ
ЕСТЬ(Номенклатура.Реквизит, Тип("СправочникСсылка.Контрагенты"))
И Номенклатура.Реквизит = &Контрагент
Использование такой конструкции делает запрос более безопасным, но может незначительно снизить производительность на больших объемах данных, так как требует дополнительного анализа типов каждой строки. Используйте этот подход только тогда, когда структура данных действительно неоднородна.
Оптимизация запросов с проверкой на NULL
Проверка на NULL является достаточно дорогой операцией с точки зрения оптимизатора запросов СУБД (MS SQL, PostgreSQL). В отличие от сравнения с конкретным значением, которое может эффективно использовать обычный индекс, проверка на отсутствие значения требует специального подхода к индексированию.
В современных версиях 1С:Предприятие 8.3 и выше платформа автоматически создает специальные индексы для полей, которые часто проверяются на пустоту, если это предусмотрено конфигурацией. Однако разработчик должен понимать, что условие ЕСТЬ NULL может приводить к полному сканированию таблицы (Table Scan), если индекс не оптимизирован под такую выборку.
Для ускорения таких запросов рекомендуется:
- 📊 Анализировать план выполнения запроса через консоль администрирования или
Консоль запросов. - 🚀 Избегать использования функций над полями в условии
ГДЕ, напримерЕСТЬNULL(Поле, ЗНАЧЕНИЕ(...)), так как это отключает использование индексов. - 🗂️ Создавать составные индексы, где поле, проверяемое на
NULL, стоит первым или вторым в зависимости от селективности выборки.
Если вы работаете с большими таблицами (регистры накопления, таблицы документов), и выборка по пустым ссылкам возвращает менее 10-15% записей, индекс будет эффективен. Если же пустых ссылок большинство (например, 80% документов не имеют заполненного поля), оптимизатор СУБД может сам решить игнорировать индекс и просканировать таблицу целиком, что в данном случае будет быстрее.
Почему NULL не равен NULL?
В реляционной модели данных NULL означает "неизвестное значение". Логически невозможно утверждать, что одно неизвестное значение равно другому неизвестному. Поэтому результат сравнения NULL = NULL всегда неизвестен (UNKNOWN), а не ИСТИНА. Для проверки используется специальный предикат IS NULL.
Также стоит упомянуть о влиянии блокировок. При выборке данных с проверкой на пустоту в высоконагруженных системах убедитесь, что вы используете правильные уровни изоляции транзакций, чтобы чтение не блокировалось активными записями, которые в данный момент заполняют эти поля.
Типичные ошибки и способы их решения
Даже опытные разработчики иногда допускают ошибки при работе с незаполненными полями. Чаще всего проблемы возникают при смешивании логики языка 1С и синтаксиса запроса, а также при некорректной обработке данных, пришедших из внешних источников.
Одна из частых ошибок — попытка использовать функцию ЗНАЧЕНИЕ для создания пустой ссылки внутри текста запроса. Конструкция ЗНАЧЕНИЕ(СправочникСсылка.Контрагенты) создает ссылку на несуществующий объект (пустую ссылку как объект 1С), но в контексте запроса к базе данных это может интерпретироваться неоднозначно в зависимости от контекста использования.
В таблице ниже приведены распространенные сценарии и правильные способы их реализации:
| Задача | Неправильный подход | Правильный подход | Результат |
|---|---|---|---|
| Фильтр по пустому полю | Поле = NULL |
Поле ЕСТЬ NULL |
Возврат строк с пустым значением |
| Опциональный параметр | Поле = &Параметр |
(&Параметр ЕСТЬ NULL ИЛИ Поле = &Параметр) |
Универсальный фильтр |
| Заполнение из кода | Параметр = "" |
Параметр = Неопределено |
Корректная передача NULL |
| Составной тип | Поле = 0 |
Поле ЕСТЬ NULL |
Избежание ошибки типов |
Еще одна проблема — работа с внешними источниками данных (ODBC, текстовые документы). При загрузке данных в 1С пустые строки из CSV-файла не всегда автоматически превращаются в NULL. Иногда они становятся пустыми строками "". В таком случае проверка ЕСТЬ NULL не сработает. Необходимо предварительно очищать данные или использовать условие Поле ЕСТЬ NULL ИЛИ Поле = "".
⚠️ Внимание: Интерфейс и возможности отладки запросов могут отличаться в разных версиях платформы 1С. Всегда проверяйте актуальность синтаксических функций в справочной системе вашей конкретной версии конфигурации.
☑️ Проверка запроса на пустые ссылки
Сравнение пустых ссылок в коде и в запросе
Важно различать контекст выполнения проверки. В коде на языке 1С (клиент или сервер) мы часто используем функцию ПустаяСсылка() или прямое сравнение с Неопределено. Однако внутри текста запроса эти конструкции недоступны или работают иначе.
Функция ПустаяСсылка(ТипСсылки) возвращает объект ссылки, у которого внутренний идентификатор равен нулю. В запросе такой объект, переданный как параметр, будет обработан корректно только если в условии используется сравнение на равенство, но сам параметр должен быть именно объектом, а не текстовым представлением.
Если вы формируете динамический запрос и подставляете значения напрямую в текст (что не рекомендуется из-за риска SQL-инъекций и проблем с кэшированием), то представление пустой ссылки нужно получать через метод ПолучитьПредставление() или использовать специальные константы, но надежнее всегда использовать параметризированный запрос.
// В коде 1С
Если ПустаяСсылка(МойОбъект.Ссылка) Тогда
// Логика для пустой ссылки
КонецЕсли;
// В запросе
// ... ГДЕ Таблица.Поле ЕСТЬ NULL
Различие критично при отладке. Если в отладчике вы видите, что переменная равна Неопределено, а запрос не находит записей, проверьте, как именно эта переменная передается в контекст запроса. Возможно, она преобразуется в строку "Неопределено" вместо значения NULL базы данных.
Всегда используйте параметризированные запросы для передачи значений. Это гарантирует корректное преобразование типа 1С "Неопределено" в тип СУБД "NULL".
Часто задаваемые вопросы (FAQ)
Можно ли использовать функцию ЕСТЬNULL() внутри запроса 1С?
Да, функция ЕСТЬNULL(Выражение, ЗаменяющееЗначение) существует в языке запросов 1С. Она возвращает ЗаменяющееЗначение, если Выражение равно NULL, иначе возвращает само Выражение. Однако использовать её в условии ГДЕ для фильтрации не рекомендуется, так как применение функции к полю таблицы часто отключает использование индексов, что замедляет работу базы данных.
В чем разница между ПустаяСсылка() и NULL в запросе?
ПустаяСсылка() — это функция языка 1С, возвращающая объект-ссылку с нулевым UUID. NULL — это состояние поля в базе данных, означающее отсутствие значения. При передаче результата ПустаяСсылка() в параметр запроса, платформа обычно корректно мапит его на NULL, но концептуально это разные сущности: одна есть в памяти процесса 1С, другая — в хранилище данных.
Почему запрос с "Поле = NULL" не выдает ошибку, но возвращает 0 строк?
Это поведение определено стандартом SQL и поддерживается 1С. Сравнение любого значения (включая NULL) с NULL через оператор равенства возвращает логическое значение "НЕИЗВЕСТНО" (UNKNOWN). В условии ГДЕ отбираются только строки, где условие истинно (TRUE). Поскольку UNKNOWN не равно TRUE, строки не попадают в выборку. Ошибки синтаксиса нет, логика просто не срабатывает.
Как найти все документы, где не заполнено подтабличное поле?
Для этого нужно использовать соединение (ЛЕВОЕ СОЕДИНЕНИЕ) с табличной частью. Если при соединении строка из основной таблицы не находит соответствия в подтаблице, поля подтаблицы будут равны NULL. Условие ГДЕ Подтаблица.Реквизит ЕСТЬ NULL после левого соединения отфильтрует именно те документы, у которых нет строк с заполненным реквизитом (или нет строк вовсе, в зависимости от типа соединения).
Влияет ли версия платформы 1С на работу с NULL?
Базовый принцип работы с NULL не менялся долгое время. Однако в новых версиях платформы (8.3.20+) улучшился оптимизатор запросов, который лучше обрабатывает условия с ЕСТЬ NULL и эффективнее использует индексы. Также появились новые функции работы с типами, которые могут упростить написание сложных условий фильтрации составных типов.