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