Как добавить условие в запрос 1С: синтаксис и примеры
Содержание
Условие в запросе 1С добавляют в секцию ГДЕ — она идёт сразу после ИЗ и всех соединений. Простейший случай: ВЫБРАТЬ Ссылка ИЗ Документ.РеализацияТоваровУслуг ГДЕ Проведён = ИСТИНА. Для фильтра по соединённой таблице условие ставят в секцию ПО, а для условия по агрегатам (после группировки) — в ИМЕЮЩИЕ.
На практике отбор редко бывает одной строкой. Приходится комбинировать И, ИЛИ, НЕ, ловить диапазоны дат, искать подстроку, отсеивать пустые ссылки и значения NULL после левых соединений. Ошибка в любом из этих мест тихо меняет результат: запрос не падает, а просто возвращает не те строки.
Ниже — все способы задать условие в языке запросов 1С:Предприятие 8.3, с рабочими примерами и разбором мест, где обычно теряют строки.
Где в тексте запроса стоит отбор
Секции запроса идут в строгом порядке. Нарушишь — конструктор или платформа выдадут синтаксическую ошибку:
ВЫБРАТЬ— поля результата;ИЗ— источник и соединения (СОЕДИНЕНИЕ,ЛЕВОЕ СОЕДИНЕНИЕ);ГДЕ— отбор строк источника;СГРУППИРОВАТЬ ПО— группировка;ИМЕЮЩИЕ— отбор по агрегатам (СУММА,КОЛИЧЕСТВО);УПОРЯДОЧИТЬ ПО— сортировка.
ГДЕ отбирает строки до группировки, ИМЕЮЩИЕ — после. В условии сравнения работают операторы =, <> (не равно), >, <, >=, <=.
ВЫБРАТЬ
Товары.Номенклатура,
Товары.Количество
ИЗ
Документ.ПоступлениеТоваровУслуг.Товары КАК Товары
ГДЕ
Товары.Количество > 0
И Товары.Ставка = Значение(Справочник.СтавкиНДС.НДС20)
При сравнении с предопределёнными элементами справочников, значениями перечислений или пустой ссылкой используют функцию ЗНАЧЕНИЕ():
ГДЕ Статус = Значение(Перечисление.СтатусыЗаказов.Выполнен)
И Контрагент <> Значение(Справочник.Контрагенты.ПустаяСсылка)
Внутри условия допустимо и вычисляемое выражение ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ — удобно, когда порог зависит от других полей строки.
Если выбираете из регистра накопления или сведений, отбор по измерению лучше задавать в параметрах виртуальной таблицы Остатки(&НаДату, Склад = &Склад), а не в ГДЕ. Платформа применит его до расчёта итогов, и запрос отработает заметно быстрее.
Несколько условий: И, ИЛИ, НЕ и скобки
Логические операторы: И (оба истинны), ИЛИ (хотя бы одно), НЕ (отрицание). У них разный приоритет — НЕ выполняется первым, затем И, в последнюю очередь ИЛИ.
Из-за этого классическая ловушка выглядит так:
ГДЕ Контрагент = &Контрагент
И Сумма > 100000
ИЛИ Валюта = &Доллары
Читается это не так, как кажется. Платформа понимает как (Контрагент И Сумма) ИЛИ Валюта, и в результат попадут все валютные строки по любым контрагентам. Группируйте связанные условия скобками — это единственный надёжный способ задать приоритет.
ГДЕ Контрагент = &Контрагент
И (Сумма > 100000 ИЛИ Валюта = &Доллары)
Теперь отбираются строки одного контрагента, где сумма больше 100 000 либо валюта — доллары. Не экономьте на скобках даже там, где приоритет очевиден: читать такой запрос через полгода будет проще.
Диапазоны и списки: МЕЖДУ и В
Для диапазона есть оператор МЕЖДУ ... И .... Границы включаются в обе стороны:
ГДЕ Товары.Количество МЕЖДУ 10 И 100
Это строго равно Количество >= 10 И Количество <= 100.
Для перечисления значений — оператор В. Он принимает список в скобках, передаваемый массив (или список значений) и даже вложенный подзапрос:
// явный список
ГДЕ Статус В (&СтатусНовый, &СтатусВРаботе)
// массив в параметре — удобно для динамических отборов
ГДЕ Номенклатура В (&МассивНоменклатуры)
// подзапрос
ГДЕ Контрагент В (
ВЫБРАТЬ Ссылка ИЗ Справочник.Контрагенты ГДЕ ЮрЛицо = ИСТИНА)
Для иерархических справочников есть В ИЕРАРХИИ — отбирает элемент и всё, что лежит под ним по дереву групп:
ГДЕ Номенклатура В ИЕРАРХИИ (&ГруппаТоваров)
Отрицание записывается как НЕ В (...). Учтите: если в списке окажется NULL, оператор НЕ В может отбросить строки неожиданно — об этом в разделе про NULL.
Вариант В (подзапрос) удобен, но на больших объёмах его часто выгоднее переписать через внутреннее соединение с тем же подзапросом: соединение платформа оптимизирует лучше, чем коррелированную проверку вхождения. Короткий фиксированный список значений, наоборот, оставляйте прямо в В (...) — так читабельнее.
Поиск по части строки: ПОДОБНО и маски
Точное совпадение задаёт =, но чаще нужно найти вхождение подстроки. Для этого — оператор ПОДОБНО с маской. Спецсимволы маски:
| Символ | Что заменяет |
|---|---|
% | любое количество любых символов (в т. ч. ноль) |
_ | ровно один любой символ |
[абв] | один символ из набора |
[а-я] | один символ из диапазона |
[^абв] | один символ, которого нет в наборе |
// наименования, содержащие «кабель»
ГДЕ Номенклатура.Наименование ПОДОБНО "%кабель%"
// артикул из буквы и трёх цифр
ГДЕ Товары.Артикул ПОДОБНО "_[0-9][0-9][0-9]"
Если искомая подстрока сама содержит %, _ или [, экранируйте её. Задайте символ экранирования через СПЕЦСИМВОЛ:
ГДЕ Комментарий ПОДОБНО "%50!%%" СПЕЦСИМВОЛ "!"
Здесь !% означает буквальный знак процента, а последний % остаётся маской.
Чтобы поиск точно не зависел от регистра, приведите обе стороны к верхнему регистру функцией ВРег():
ГДЕ ВРег(Контрагент.Наименование) ПОДОБНО ВРег("%ооо%")
ПОДОБНО не умеет искать по неограниченным строкам (тип «Строка» без указания длины, например в полях комментариев документов). Приведите поле к ограниченной длине через ВЫРАЗИТЬ(Комментарий КАК Строка(500)), иначе платформа выдаст ошибку.
Сравнение с NULL: почему знак равенства не работает
NULL в запросах — это «значение отсутствует», и оно не равно ничему, даже другому NULL. Любое сравнение с NULL через = или <> возвращает не «истину» и не «ложь», а третье состояние — «неизвестно». Строки в состоянии «неизвестно» секция ГДЕ не пропускает.
Поэтому условие ГДЕ Поле = NULL не вернёт ничего, даже если пустые значения в таблице есть. Правильные операторы — ЕСТЬ NULL и ЕСТЬ НЕ NULL:
ГДЕ Заказ.Договор ЕСТЬ NULL // строки без договора
ГДЕ Заказ.Договор ЕСТЬ НЕ NULL // только с заполненным договором
Чтобы в самом результате заменить NULL на значение по умолчанию, используйте функцию ЕСТЬNULL(поле, значение_если_пусто):
ВЫБРАТЬ ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток
Откуда вообще берётся NULL? Чаще всего — из левых соединений: если у строки левой таблицы нет пары в правой, все поля правой таблицы в результате будут NULL. Второй источник — виртуальные таблицы остатков, где по позиции не было движений.
Трёхзначная логика бьёт и по отрицаниям. Условие НЕ (Поле = &Значение) не вернёт строки, где Поле равно NULL: внутреннее сравнение даёт «неизвестно», а отрицание от «неизвестно» — снова «неизвестно». Если такие строки нужны, пишите явно: НЕ (Поле = &Значение) ИЛИ Поле ЕСТЬ NULL. То же касается и <>: строка с пустым полем через Поле <> &Значение не пройдёт, хотя интуитивно кажется, что должна.
Условия с датами и периодами
Дату-константу в тексте запроса задают литералом ДАТАВРЕМЯ(год, месяц, день, час, минута, секунда):
ГДЕ Документ.Дата >= ДАТАВРЕМЯ(2024, 1, 1)
Но в реальном коде границы периода обычно приходят параметрами. И здесь прячется самая частая ошибка с датами. Дата в 1С хранит и время, поэтому 31.01.2024 по умолчанию — это 31.01.2024 00:00:00. Условие Дата <= &ДатаКонца отрежет все документы за последний день, кроме сделанных ровно в полночь.
Решение — функция КОНЕЦПЕРИОДА, которая возвращает последнее мгновение периода (23:59:59):
ГДЕ Документ.Дата МЕЖДУ НАЧАЛОПЕРИОДА(&ДатаНачала, ДЕНЬ)
И КОНЕЦПЕРИОДА(&ДатаКонца, ДЕНЬ)
НАЧАЛОПЕРИОДА и КОНЕЦПЕРИОДА принимают период: ДЕНЬ, НЕДЕЛЯ, МЕСЯЦ, КВАРТАЛ, ГОД. Для сдвига даты есть ДОБАВИТЬКДАТЕ(дата, ДЕНЬ, 7), для разницы — РАЗНОСТЬДАТ(дата1, дата2, ДЕНЬ).
Отбор «за текущий месяц» без передачи параметров: ГДЕ Дата МЕЖДУ НАЧАЛОПЕРИОДА(&ТекДата, МЕСЯЦ) И КОНЕЦПЕРИОДА(&ТекДата, МЕСЯЦ), где &ТекДата — ТекущаяДата() из встроенного языка.
Условия в соединении таблиц: ПО и ГДЕ
При соединении таблиц условие связи пишут в секции ПО. Это ключ, по которому строки одной таблицы сопоставляются со строками другой:
ИЗ Справочник.Номенклатура КАК Ном
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Цены.СрезПоследних КАК Цены
ПО Цены.Номенклатура = Ном.Ссылка
Теперь тонкий момент, на котором спотыкаются и опытные разработчики. Если к левому соединению нужно добавить фильтр по правой таблице, место фильтра меняет результат:
- условие в
ПО— применяется до соединения, строки левой таблицы сохраняются; - условие в
ГДЕ— применяется после, и строки сNULLв правой таблице отсекаются, превращая левое соединение во внутреннее.
// ПРАВИЛЬНО: номенклатура останется вся, цена подтянется только по рознице
...ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Цены.СрезПоследних КАК Цены
ПО Цены.Номенклатура = Ном.Ссылка И Цены.ТипЦен = &Розница
Если тот же фильтр Цены.ТипЦен = &Розница поставить в ГДЕ, из результата пропадёт вся номенклатура без розничной цены. Иногда это и нужно — тогда фильтр в ГДЕ оправдан. Но решение должно быть осознанным.
Параметры в условиях вместо подстановки значений
Любое значение в условии стоит передавать параметром &Имя, а не склеивать текст запроса из строк:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ Ссылка ИЗ Документ.ЗаказПокупателя
|ГДЕ Контрагент = &Контрагент И Дата >= &Начало";
Запрос.УстановитьПараметр("Контрагент", ВыбранныйКонтрагент);
Запрос.УстановитьПараметр("Начало", НачалоГода);
Это не только чище, но и безопаснее: платформа сама подставит значение нужного типа, кэширует план запроса и избавит от ошибок экранирования. В параметр можно положить массив — тогда он подставляется в оператор В.
Частый приём — необязательное условие. Если параметр может быть не заполнен, добавьте проверку на пустое значение прямо в текст:
ГДЕ (&Склад = Значение(Справочник.Склады.ПустаяСсылка) ИЛИ Склад = &Склад)
Когда склад не выбран, первая часть условия истинна и фильтр по складу отключается; когда выбран — работает отбор. Так один запрос обслуживает отчёт и с фильтром, и без.
Следите за типом параметра. Если УстановитьПараметр не вызвать вовсе, параметр получит тип NULL, и сравнение с ним провалится так же, как любое сравнение с NULL. Для пустой ссылки передавайте именно пустую ссылку нужного типа (Справочники.Склады.ПустаяСсылка()), а не Неопределено — иначе условие отбора по умолчанию поведёт себя не так, как рассчитывали.
Частые вопросы
Чем ГДЕ отличается от ИМЕЮЩИЕ?
ГДЕ фильтрует отдельные строки источника до группировки. ИМЕЮЩИЕ фильтрует уже сгруппированные данные по агрегатам. Отбор «сумма по контрагенту больше миллиона» можно задать только в ИМЕЮЩИЕ: ИМЕЮЩИЕ СУММА(Сумма) > 1000000.Можно ли в ГДЕ сослаться на псевдоним поля из ВЫБРАТЬ?
ГДЕ псевдонимов из ВЫБРАТЬ ещё не существует. Повторите выражение целиком в условии либо оберните запрос во вложенный и фильтруйте снаружи по псевдониму.Регистрозависим ли оператор ПОДОБНО?
ВРег().Почему условие в параметрах виртуальной таблицы быстрее, чем в ГДЕ?
ГДЕ отрабатывает уже по готовой выборке. На больших регистрах разница в скорости — кратная.Как отобрать строки, где поле входит в список, но без NULL-эффекта?
НЕ В (...) добавьте явную проверку И Поле ЕСТЬ НЕ NULL, если в поле возможны пустые значения. Иначе строки с NULL могут не попасть в результат из-за трёхзначной логики сравнения.