Отбор данных в запросе: как задать условие через ГДЕ
Содержание
Условие отбора в запросе задаёт оператор ГДЕ — он идёт после ИЗ и оставляет только те строки, для которых выражение истинно. Пишется так: ГДЕ Номенклатура.Цена > 100. Значения из программы подставляют через параметры с префиксом &, строки сравнивают через ПОДОБНО, диапазоны — через МЕЖДУ, список значений — через В.
Ниже — все операторы, которые используют в условиях языка запросов 1С 8.3: сравнение, логические связки И/ИЛИ/НЕ, шаблоны строк, отбор по датам, проверка на NULL и пустые ссылки. В конце — приёмы, которые не дают индексам работать, и как их обойти.
Оператор ГДЕ и структура условия
Запрос читает данные из таблиц, перечисленных в ИЗ, а ГДЕ отсеивает лишние строки. Минимальный скелет выглядит так:
ВЫБРАТЬ
Товары.Номенклатура,
Товары.Количество
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Товары
ГДЕ
Товары.Количество > 0
Выражение после ГДЕ должно давать логическое значение — Истина или Ложь. Можно сравнивать поле с числом, строкой, датой, ссылкой или с другим полем той же выборки.
Доступные операторы сравнения:
- 🟰
=— равно; <>— не равно;>и<— больше / меньше;>=и<=— больше или равно / меньше или равно.
Текст запроса удобно собирать и отлаживать в Конструкторе запроса — он есть в контекстном меню модуля (пункт про конструктор запроса) и на закладке «Условия» формирует ГДЕ без ручного набора.
Одно выражение редко закрывает задачу, поэтому условия соединяют логическими связками — об этом дальше.
Параметры запроса вместо значений в тексте
Жёстко вписывать значения в текст запроса нельзя — ни число, ни строку, ни дату. Для этого есть параметры: имя с амперсандом внутри запроса и метод УстановитьПараметр снаружи.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.Наименование
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.ЮрФизЛицо = &ВидКонтрагента";
Запрос.УстановитьПараметр("ВидКонтрагента", ПредопределенноеЗначение("Перечисление.ЮрФизЛицо.ЮридическоеЛицо"));
Результат = Запрос.Выполнить();
Почему именно параметры, а не склейка строк:
- 🔒 значение любого типа передаётся как есть — ссылка остаётся ссылкой, дата датой, не нужно форматировать;
- ⚡ платформа кэширует разобранный текст запроса — при повторных вызовах с разными параметрами он не парсится заново;
- 🛡️ исключается ошибка с кавычками и спецсимволами в пользовательском вводе.
Имя параметра в тексте (&ВидКонтрагента) и строка в УстановитьПараметр должны совпадать посимвольно — иначе параметр останется неустановленным, и платформа подставит NULL, из-за чего условие тихо вернёт пустую выборку.
Параметр может быть и списком значений (Новый СписокЗначений или массив) — тогда его используют вместе с оператором В, о котором ниже.
Связки И, ИЛИ, НЕ и скобки
Несколько условий объединяют связками И, ИЛИ, НЕ. У них разный приоритет: сначала вычисляется НЕ, затем И, в последнюю очередь ИЛИ. Из-за этого порядок без скобок часто даёт не тот результат, что ожидался.
Сравните два варианта:
// Без скобок: берёт проведённые по складу А плюс ВСЕ по складу Б
ГДЕ Док.Проведён И Док.Склад = &СкладА ИЛИ Док.Склад = &СкладБ
// Со скобками: проведённые по складу А или Б
ГДЕ Док.Проведён И (Док.Склад = &СкладА ИЛИ Док.Склад = &СкладБ)
Если в условии встречается хотя бы один ИЛИ, ставьте скобки вокруг него. Это снимает зависимость от приоритета операций и делает логику читаемой.
Оператор НЕ инвертирует условие: НЕ Док.Проведён оставит непроведённые. Но с НЕ есть тонкость — он плохо дружит с NULL: выражение НЕ (Поле = 5) не вернёт строки, где Поле равно NULL, потому что сравнение с NULL даёт не Ложь, а неопределённость. Этот случай разберём в разделе про пустые значения.
Сравнение строк: ПОДОБНО и шаблоны
Точное совпадение строки задаётся через =, а поиск по части текста — оператором ПОДОБНО (аналог LIKE в SQL). В шаблоне работают спецсимволы:
| Символ | Что означает | Пример | Найдёт |
|---|---|---|---|
% | любое число любых символов | "%молоко%" | строки, где есть «молоко» |
_ | ровно один любой символ | "АВ_01" | АВ001, АВ101, АВx01 |
[абв] | один символ из набора | "[АБ]%" | начинается на А или Б |
[а-я] | один символ из диапазона | "[0-9]%" | начинается с цифры |
[^абв] | один символ не из набора | "[^0-9]%" | начинается не с цифры |
Пример отбора номенклатуры, в наименовании которой встречается подстрока:
ГДЕ Номенклатура.Наименование ПОДОБНО &Шаблон
// в параметр передаём "%" + ТекстПоиска + "%"
Если сам символ % или _ нужно искать как обычный, его экранируют через СПЕЦСИМВОЛ:
ГДЕ Строка ПОДОБНО "%30\%%" СПЕЦСИМВОЛ "\"
// найдёт строки, где есть "30%"
ПОДОБНО чувствителен к регистру в зависимости от настроек базы, а по умолчанию сравнение в запросах регистронезависимое. Если нужно строго учесть регистр, используйте проверку через функции работы со строками на стороне кода.
Отдельно про скорость: шаблон, который начинается с % ("%текст%"), не может использовать индекс — платформа читает всю таблицу. Это нормально для разовой выгрузки и плохо для частого отбора по большой таблице. Когда возможно, ищите по началу строки ("текст%") — такой вариант индекс использует.
Диапазоны, списки и иерархия: МЕЖДУ, В, В ИЕРАРХИИ
Три оператора закрывают типовые случаи, которые иначе пришлось бы писать длинной цепочкой ИЛИ.
МЕЖДУ задаёт диапазон, границы включаются:
ГДЕ Док.Дата МЕЖДУ &ДатаНачала И &ДатаКонца
МЕЖДУ 10 И 20 — это то же, что >= 10 И <= 20. Удобно для сумм, количеств и дат.
В проверяет вхождение в список значений:
ГДЕ Номенклатура.ВидНоменклатуры В (&СписокВидов)
В параметр СписокВидов передают массив или СписокЗначений. Можно перечислить значения и прямо в тексте: В (&Вид1, &Вид2). Отрицание — НЕ В (...), но его применяйте осторожно: по большой таблице оно работает медленнее, чем В.
В ИЕРАРХИИ отбирает элемент и всё, что лежит под ним в иерархическом справочнике:
ГДЕ Номенклатура.Ссылка В ИЕРАРХИИ (&ГруппаТоваров)
Строка попадёт в выборку, если номенклатура — это сама группа или любой её элемент на любом уровне вложенности. Без В ИЕРАРХИИ пришлось бы отдельно обходить подгруппы.
| Задача | Оператор | Чем заменяет |
|---|---|---|
| попадание в диапазон | МЕЖДУ a И b | >= a И <= b |
| одно из перечисленных значений | В (...) | длинную цепочку ИЛИ |
| элемент и его подгруппы | В ИЕРАРХИИ (...) | ручной обход дерева |
Отбор по датам и периодам
Дата в запросе — это момент времени с точностью до секунды. Литерал даты задаётся функцией ДАТАВРЕМЯ:
ГДЕ Док.Дата >= ДАТАВРЕМЯ(2024, 1, 1)
// можно с временем: ДАТАВРЕМЯ(2024, 1, 1, 9, 0, 0)
Главная ловушка — граница периода. Условие Дата <= ДАТАВРЕМЯ(2024,1,31) отбросит документы за 31 января, у которых время больше 00:00:00. Чтобы захватить весь день, берут начало следующего дня со строгим < или функцию КОНЕЦПЕРИОДА:
ГДЕ Док.Дата МЕЖДУ НАЧАЛОПЕРИОДА(&Дата, ДЕНЬ) И КОНЕЦПЕРИОДА(&Дата, ДЕНЬ)
КОНЕЦПЕРИОДА(..., ДЕНЬ) возвращает 23:59:59 указанного дня, поэтому МЕЖДУ с ним захватит все документы суток. Период можно указать ДЕНЬ, НЕДЕЛЯ, МЕСЯЦ, КВАРТАЛ, ГОД.
Полезные функции для условий по датам:
- 📅
НАЧАЛОПЕРИОДА(Дата, МЕСЯЦ)— первое число месяца, 00:00:00; - 📅
КОНЕЦПЕРИОДА(Дата, МЕСЯЦ)— последняя секунда месяца; - ➕
ДОБАВИТЬКДАТЕ(Дата, ДЕНЬ, -7)— сдвиг на 7 дней назад; - 🔢
ГОД(Дата),МЕСЯЦ(Дата),ДЕНЬ(Дата)— извлечь часть даты.
Функции над полем (ГОД(Док.Дата) = 2024) удобны, но мешают использовать индекс по дате. Для отбора за год лучше Док.Дата МЕЖДУ НАЧАЛОПЕРИОДА(&Дата, ГОД) И КОНЕЦПЕРИОДА(&Дата, ГОД) — здесь поле сравнивается напрямую.
Пустая дата в 1С — это ДАТАВРЕМЯ(1, 1, 1). Проверка «дата не заполнена» пишется как Док.ДатаОплаты = ДАТАВРЕМЯ(1, 1, 1).
Пустые значения, NULL и проверка ссылок
Два похожих, но разных понятия путают чаще всего: пустая ссылка и NULL.
Пустая ссылка — это заполненное, но «нулевое» значение реквизита (ссылка никуда не указывает). В запросе её задают через ЗНАЧЕНИЕ:
ГДЕ Товары.Склад = ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка)
NULL появляется, когда соединения левым/правым СОЕДИНЕНИЕ не нашли пару, или когда поля вовсе нет. NULL нельзя сравнивать через = — любое сравнение с ним даёт неопределённость, а не Истину. Поэтому используют специальный синтаксис:
ГДЕ Остатки.КоличествоОстаток ЕСТЬ НЕ NULL // есть значение
ГДЕ Продажи.Сумма ЕСТЬ NULL // значения нет
Чтобы подменить NULL на конкретное значение прямо в выборке, применяют функцию ЕСТЬNULL:
ВЫБРАТЬ ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Количество
Это особенно важно в условиях после соединения: ГДЕ ЕСТЬNULL(Остатки.Количество, 0) > 0 отработает корректно, а ГДЕ Остатки.Количество > 0 пропустит строки, где количество оказалось NULL.
Отдельная задача — поле составного типа (например, «Регистратор» или реквизит с несколькими допустимыми типами). Прежде чем обращаться к его реквизитам, тип уточняют оператором ССЫЛКА или приводят через ВЫРАЗИТЬ:
ГДЕ Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент
ССЫЛКА в условии проверяет, что значение относится к указанному типу; ВЫРАЗИТЬ приводит поле к типу, чтобы достать его реквизиты. Без приведения обращение Регистратор.Контрагент по составному полю вернёт NULL для чужих типов.
Что замедляет отбор и как ускорить
Условие бывает правильным по результату, но тяжёлым по скорости. Разница на большой базе — секунды против минут. Основные причины и что делать:
- 🔎 Функция над полем.
ВЫРАЗИТЬ(...),ГОД(Поле),ПОДОБНО "%...%"в левой части условия не дают использовать индекс. Сравнивайте само поле: вместоГОД(Дата)=2024— диапазон поДата. - 📋 Отбор по виртуальной таблице в ГДЕ. Для таблиц остатков и оборотов условие нужно задавать в параметрах виртуальной таблицы
(, Склад = &Склад), а не вГДЕпосле выборки — тогда платформа отфильтрует данные на этапе чтения, а не после. - 🚫 НЕ В и НЕ по большим спискам. Отрицание часто приводит к полному перебору таблицы. Где возможно, формулируйте условие через
ВбезНЕ. - 🔗 Отбор по полю через точку.
ГДЕ Товар.Поставщик.Регион = &Регион— это неявное соединение. По возможности выносите такие условия в явные соединения или добавляйте реквизит в индекс.
Нажмите в консоли запросов или в технологическом журнале кнопку получения плана — он покажет, читается индекс или вся таблица. Если видите полное сканирование по большой таблице, ищите в условии функцию над полем.
Общее правило: условие должно сравнивать индексированное поле (Ссылка, Период, Регистратор, измерения регистров) напрямую с параметром или константой. Чем проще левая часть, тем выше шанс, что СУБД использует индекс.
Частые ошибки в условиях запроса
Ошибки, которые дают не синтаксический сбой, а тихо неверную выборку — их труднее всего заметить:
- ❌ Сравнение ссылки со строкой.
ГДЕ Контрагент = "ООО Ромашка"всегда ложно: слева ссылка, справа строка. Сравнивайте со ссылкой через параметр. - ❌ = NULL вместо ЕСТЬ NULL.
ГДЕ Поле = NULLне вернёт ничего. ТолькоЕСТЬ NULL/ЕСТЬ НЕ NULL. - ❌ Потеря строк из-за NULL после соединения. Условие
> 0по полю изЛЕВОГО СОЕДИНЕНИЯотсекает несопоставленные строки. Оборачивайте вЕСТЬNULL(Поле, 0). - ❌ Граница периода без конца дня.
<= ДАТАВРЕМЯ(2024,1,31)теряет документы этого дня со временем. БеритеКОНЕЦПЕРИОДА. - ❌ ИЛИ без скобок. Приоритет
ИвышеИЛИ, результат смещается. Скобки обязательны.
Частые вопросы
Можно ли в условии сравнивать два поля одной таблицы?
ГДЕ Товары.Количество > Товары.Зарезервировано — допустимое условие. Поля должны быть сравнимых типов.Чем отличается В от В ИЕРАРХИИ?
В (...) ищет точное совпадение с перечисленными значениями. В ИЕРАРХИИ (...) дополнительно захватывает все элементы, лежащие внутри указанной группы иерархического справочника.Почему условие с параметром вернуло пустую выборку, хотя данные есть?
УстановитьПараметр, и платформа подставила NULL. Сравнение с NULL ложно для любой строки — отбор пуст. Сверьте написание посимвольно.Как отобрать строки с незаполненным реквизитом-ссылкой?
ГДЕ Реквизит = ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка). Это именно пустая ссылка, а не NULL.Нужно ли экранировать кавычки в значении для ПОДОБНО?
СПЕЦСИМВОЛ нужно только символы шаблона % и _, если они должны искаться как обычные.