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