Как в запросе 1С проверить поле на заполненность
Содержание
Чтобы в запросе проверить поле на заполненность, выбор зависит от того, что именно пусто. Для полей из присоединённых таблиц (ЛЕВОЕ СОЕДИНЕНИЕ) и пропущенных данных регистров работают операторы ЕСТЬ NULL и ЕСТЬ НЕ NULL. Для пустых ссылок справочников и документов — сравнение со ЗНАЧЕНИЕ(Справочник.Имя.ПустаяСсылка). Для строк и дат — сравнение с "" и ДАТАВРЕМЯ(1,1,1).
Главная ошибка новичка — писать ГДЕ Поле = NULL. Это условие не отберёт ни одной строки никогда. NULL нельзя сравнивать знаком равенства, для него есть отдельный оператор. Ниже — как устроена пустота в языке запросов, чем ЕСТЬ NULL отличается от функции ЕСТЬNULL, как это влияет на индексы и какие ловушки встречаются чаще всего.
Три разных пустоты: NULL, Неопределено и пустое значение
В языке запросов 1С «пусто» — это не одно состояние, а минимум три разных, и путать их нельзя.
🔹 NULL — отсутствие данных как таковых. Поля нет, строки нет, присоединение не нашло пару. NULL не равен нулю, не равен пустой строке и не равен сам себе.
🔹 Неопределено (NULL ≠ Неопределено) — специальное значение составного типа. Появляется, например, когда поле составного типа не заполнено, или при разыменовании реквизита у пустой ссылки.
🔹 Пустое значение типа — заполненное поле, но «нулевое» для своего типа: пустая ссылка справочника, строка "", число 0, пустая дата ДАТАВРЕМЯ(1,1,1).
Разница практическая. Если поле Поставщик в документе просто не выбрали — там лежит пустая ссылка, а не NULL. А вот если вы присоединяете к этому документу регистр ЛЕВЫМ СОЕДИНЕНИЕМ и записи там не оказалось — поля регистра получат именно NULL. Поэтому проверка «не выбран поставщик» и проверка «нет движения по регистру» пишутся по-разному.
Запомните правило: NULL почти всегда приходит из ЛЕВОГО/ПОЛНОГО СОЕДИНЕНИЯ и из виртуальных таблиц остатков, когда данных по измерению нет. Пустая ссылка — из самого реквизита объекта.
Откуда в результате берётся NULL
Прежде чем проверять, полезно понимать источник. NULL в выборке возникает в нескольких ситуациях:
- Левое или полное соединение без пары. Строка левой таблицы есть, а в правой совпадения нет — все поля правой таблицы станут NULL.
- Виртуальные таблицы остатков и оборотов. Если по номенклатуре не было движений,
КоличествоОстатоквернётся как NULL, а не как0. - Поле составного типа, у которого разыменовали реквизит несуществующего типа.
- Агрегатные функции
СУММА,МАКСИМУМпо пустому набору строк после группировки в отдельных сценариях.
Самый частый случай — первый. Классический запрос «остатки по всей номенклатуре, включая ту, что лежит мёртвым грузом с нулём»:
ВЫБРАТЬ
Номенклатура.Наименование КАК Наименование,
Остатки.КоличествоОстаток КАК Остаток
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
ПО Остатки.Номенклатура = Номенклатура.Ссылка
По товарам без остатка поле Остаток придёт как NULL. Если потом прибавить к нему число — получите снова NULL, а не сумму. Отсюда и растут все дальнейшие приёмы.
Синтаксис операторов ЕСТЬ NULL и ЕСТЬ НЕ NULL
Проверить, что поле пусто в смысле NULL, можно только через оператор ЕСТЬ NULL. Обратная проверка — ЕСТЬ НЕ NULL.
Найти товары, по которым вообще нет движений:
ВЫБРАТЬ
Номенклатура.Наименование КАК Наименование
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
ПО Остатки.Номенклатура = Номенклатура.Ссылка
ГДЕ
Остатки.КоличествоОстаток ЕСТЬ NULL
Зеркальный случай — только те, по кому движения были:
ГДЕ
Остатки.КоличествоОстаток ЕСТЬ НЕ NULL
Ещё один типовой приём — поиск «сирот»: документы-основания, по которым не создали дочерний документ. Например, заказы покупателей без реализации:
ВЫБРАТЬ
Заказы.Номер КАК Номер,
Заказы.Дата КАК Дата
ИЗ
Документ.ЗаказКлиента КАК Заказы
ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК Реализация
ПО Реализация.ЗаказКлиента = Заказы.Ссылка
ГДЕ
Реализация.Ссылка ЕСТЬ NULL
Проверяйте на ЕСТЬ NULL поле Ссылка правой таблицы, а не какой-то реквизит. Реквизит теоретически может быть пустым и при наличии строки, а вот Ссылка при отсутствии соединения гарантированно NULL.
Названия таблиц и реквизитов (ЗаказКлиента, РеализацияТоваровУслуг) отличаются между конфигурациями. Проверьте имена в конструкторе запроса своей базы — логика операторов от этого не меняется.
Функция ЕСТЬNULL: подстановка значения вместо пустоты
Оператор ЕСТЬ NULL только проверяет. Функция ЕСТЬNULL (в англоязычной раскладке ISNULL) — заменяет NULL на нужное значение прямо в выборке. Синтаксис:
ЕСТЬNULL(<Выражение>, <ЗначениеПоУмолчанию>)
Если <Выражение> не NULL — вернётся оно само. Если NULL — вернётся второй аргумент. Тот же запрос по остаткам, но с нулями вместо пустот:
ВЫБРАТЬ
Номенклатура.Наименование КАК Наименование,
ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
ПО Остатки.Номенклатура = Номенклатура.Ссылка
Теперь товары без движений покажут честный 0, и по полю можно спокойно считать суммы, сортировать, сравнивать с числом.
Важная деталь про типы: второй аргумент задаёт тип результата. Если написать ЕСТЬNULL(Поле, 0), поле станет числовым. Для суммовых полей подставляйте число, для дат — дату, для строк — строку. Подстановка числа туда, где ждут строку, приведёт к ошибке совместимости типов.
Когда ЕСТЬNULL не хватает — помогает конструкция ВЫБОР КОГДА. Она гибче: позволяет вернуть разные значения по разным условиям.
ВЫБОР
КОГДА Остатки.КоличествоОстаток ЕСТЬ NULL
ТОГДА "Нет на складе"
КОГДА Остатки.КоличествоОстаток = 0
ТОГДА "Нулевой остаток"
ИНАЧЕ "В наличии"
КОНЕЦ
Здесь видно разницу, которую ЕСТЬNULL стирает: NULL (движений не было вовсе) и 0 (движения были, но свелись в ноль) — это разные состояния, и отчёт иногда должен их различать.
Пустые ссылки, строки и даты — здесь NULL ни при чём
Частая путаница: разработчик пишет ГДЕ Договор ЕСТЬ NULL, рассчитывая поймать документы без договора, и получает пустой результат. Реквизит объекта не бывает NULL — если его не заполнили, там лежит пустая ссылка.
Для таких проверок нужен другой инструмент:
Пустая ссылка справочника или документа — сравнение со специальным пустым значением:
ГДЕ
Товары.Поставщик = ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка)
Обратная проверка «поставщик заполнен» — через <>:
ГДЕ
Товары.Поставщик <> ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка)
Пустая строка:
ГДЕ
Контрагенты.ИНН = ""
Пустая дата — это не NULL, а минимально возможная дата платформы:
ГДЕ
Договоры.ДатаОкончания = ДАТАВРЕМЯ(1, 1, 1)
Разберём таблицей, когда что применять.
| Что проверяем | Правильный способ | Неверно |
|---|---|---|
| Поле присоединённой таблицы | Поле ЕСТЬ NULL | Поле = NULL |
| Реквизит-ссылка объекта | = ЗНАЧЕНИЕ(…ПустаяСсылка) | ЕСТЬ NULL |
| Строковый реквизит | = "" | ЕСТЬ NULL |
| Дата | = ДАТАВРЕМЯ(1,1,1) | ЕСТЬ NULL |
Отдельный разговор — табличные части. Если нужно отобрать документы, у которых таблица товаров пуста, напрямую «количество строк» в условии ГДЕ не проверить. Приём — левое соединение с табличной частью и проверка её Ссылки на ЕСТЬ NULL:
ВЫБРАТЬ РАЗЛИЧНЫЕ
Поступление.Ссылка КАК Документ
ИЗ
Документ.ПоступлениеТоваровУслуг КАК Поступление
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПоступлениеТоваровУслуг.Товары КАК Товары
ПО Товары.Ссылка = Поступление.Ссылка
ГДЕ
Товары.Ссылка ЕСТЬ NULL
Здесь NULL снова работает по назначению — потому что речь о соединении, а не о реквизите.
Чтобы не держать всё это в голове, определите по шагам, что у вас за пустота:
ЕСТЬ NULL и ЕСТЬ НЕ NULL. Для подстановки значения — ЕСТЬNULL(Поле, 0).ЗНАЧЕНИЕ(Справочник.Имя.ПустаяСсылка). NULL тут не возникает.Поле = "". Пустая строка — не NULL.Поле = ДАТАВРЕМЯ(1, 1, 1). Пустая дата — минимальная дата, а не NULL.Ссылки на ЕСТЬ NULL.Как проверки влияют на производительность и индексы
Проверка на заполненность кажется безобидной, но на больших таблицах легко посадить запрос.
Функция поверх поля в условии ломает индекс. Если написать в ГДЕ условие вида ЕСТЬNULL(Поле, 0) = 0, СУБД не сможет использовать индекс по этому полю — ей придётся вычислять функцию для каждой строки. Это полный перебор. Для отбора по пустоте оставляйте поле «чистым»: Поле ЕСТЬ NULL индекс использовать может, ЕСТЬNULL(Поле, 0) = 0 — нет.
ЕСТЬNULL уместна в выборке, а не в отборе. Подставлять нули для вывода — нормально. Фильтровать по обёрнутому в функцию полю — плохо. Если нужен и отбор, и замена, фильтруйте по ЕСТЬ NULL / ЕСТЬ НЕ NULL, а ЕСТЬNULL используйте только в списке полей.
Проверка ЕСТЬ NULL после соединения — частый антипаттерн вместо ПРАВОЕ/подзапроса. Поиск сирот через ЛЕВОЕ СОЕДИНЕНИЕ + ЕСТЬ NULL читается просто, но на миллионах строк соединение дороже, чем отбор через НЕ В (ВЫБРАТЬ …) по индексированному полю. На небольших справочниках разница незаметна, на документообороте за годы — чувствительна.
Правило простое: проверяйте на ЕСТЬ NULL только то поле, по которому реально нет данных (обычно Ссылка правой таблицы), и не навешивайте функций на поля, по которым идёт отбор.
Типичные ошибки и как их не совершать
🔸 Поле = NULL и Поле <> NULL. Самая частая. Равенство с NULL всегда даёт неопределённый результат, строки не отбираются. Только ЕСТЬ NULL / ЕСТЬ НЕ NULL.
🔸 Проверка реквизита-ссылки через ЕСТЬ NULL. Реквизит объекта не бывает NULL, он бывает пустой ссылкой. Результат — пустая выборка, хотя незаполненные документы есть.
🔸 Арифметика с NULL без защиты. Остатки.Приход - Остатки.Расход, где одно из полей NULL, даст NULL, а не разницу. Оборачивайте каждое слагаемое: ЕСТЬNULL(Приход, 0) - ЕСТЬNULL(Расход, 0).
🔸 ЕСТЬNULL с несовместимым типом второго аргумента. ЕСТЬNULL(Дата, 0) или ЕСТЬNULL(Строка, 0) вызовут ошибку совместимости типов. Подставляйте значение того же типа, что поле.
🔸 Отбор по полю под функцией. ГДЕ ЕСТЬNULL(Сумма, 0) > 100 отрабатывает, но без индекса. Если по полю не бывает NULL — уберите обёртку; если бывает — продумайте соединение.
🔸 Путаница NULL и Неопределено в полях составного типа. Это разные значения; ЕСТЬ NULL поймает NULL, но не Неопределено. Для составных типов иногда нужны обе проверки.
Проверьте себя — три коротких вопроса на понимание:
Практические примеры из работы
Сведём приёмы в задачи, которые встречаются постоянно.
Остатки по всем товарам, включая нулевые. Левое соединение справочника с виртуальной таблицей остатков, поле обёрнуто в ЕСТЬNULL(…, 0). Иначе товары без движений либо выпадут (при внутреннем соединении), либо дадут NULL в сумме.
Контрагенты без заполненного ИНН. Это реквизит-строка, поэтому ГДЕ Контрагенты.ИНН = "". Плюс можно добавить ИЛИ Контрагенты.ИНН ЕСТЬ NULL — на случай, если поле приходит из соединения.
Договоры без даты окончания (бессрочные). ГДЕ Договоры.ДатаОкончания = ДАТАВРЕМЯ(1, 1, 1). Пустая дата — валидное значение «не указано».
Номенклатура без указанной группы или вида. Реквизит-ссылка: ГДЕ Номенклатура.ВидНоменклатуры = ЗНАЧЕНИЕ(Справочник.ВидыНоменклатуры.ПустаяСсылка).
Заказы без отгрузки. Левое соединение с документом реализации и ГДЕ Реализация.Ссылка ЕСТЬ NULL. На больших объёмах — через НЕ В (ВЫБРАТЬ ЗаказКлиента ИЗ Документ.РеализацияТоваровУслуг …).
Общая логика выбора метода одна: сначала определите, откуда взялось поле. Из соединения или виртуальной таблицы — работаете с NULL. Из самого объекта — с пустым значением типа. Перепутать эти два мира — и есть корень большинства «запрос почему-то ничего не находит».
Частые вопросы
Чем `ЕСТЬ NULL` отличается от функции `ЕСТЬNULL`?
ЕСТЬ NULL — это оператор условия, он возвращает истину или ложь и ставится в ГДЕ или ВЫБОР КОГДА. ЕСТЬNULL(Поле, Значение) — функция, она не проверяет, а подставляет указанное значение вместо NULL прямо в выбранное поле.Почему условие `ГДЕ Поле = NULL` не работает?
= или <> даёт не истину и не ложь, а неопределённый результат. Такие строки в выборку не попадают. Для NULL существует только ЕСТЬ NULL и ЕСТЬ НЕ NULL.NULL и пустая ссылка — это одно и то же?
Как посчитать разницу двух полей, если одно может быть NULL?
ЕСТЬNULL с нулём: ЕСТЬNULL(Приход, 0) - ЕСТЬNULL(Расход, 0). Без этого любое NULL-слагаемое обнулит весь результат до NULL.Замедляет ли `ЕСТЬNULL` запрос?
ГДЕ — да: функция поверх поля мешает использовать индекс, и СУБД перебирает все строки. Для отбора фильтруйте по ЕСТЬ NULL, а ЕСТЬNULL держите только в полях выборки.