Отбор данных в запросе: как задать условие через ГДЕ

· 10 мин

Содержание

Условие отбора в запросе задаёт оператор ГДЕ — он идёт после ИЗ и оставляет только те строки, для которых выражение истинно. Пишется так: ГДЕ Номенклатура.Цена > 100. Значения из программы подставляют через параметры с префиксом &, строки сравнивают через ПОДОБНО, диапазоны — через МЕЖДУ, список значений — через В.

Ниже — все операторы, которые используют в условиях языка запросов 1С 8.3: сравнение, логические связки И/ИЛИ/НЕ, шаблоны строк, отбор по датам, проверка на NULL и пустые ссылки. В конце — приёмы, которые не дают индексам работать, и как их обойти.

Оператор ГДЕ и структура условия

Запрос читает данные из таблиц, перечисленных в ИЗ, а ГДЕ отсеивает лишние строки. Минимальный скелет выглядит так:

ВЫБРАТЬ
    Товары.Номенклатура,
    Товары.Количество
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК Товары
ГДЕ
    Товары.Количество > 0

Выражение после ГДЕ должно давать логическое значение — Истина или Ложь. Можно сравнивать поле с числом, строкой, датой, ссылкой или с другим полем той же выборки.

Доступные операторы сравнения:

  • 🟰 = — равно;
  • <> — не равно;
  • > и < — больше / меньше;
  • >= и <= — больше или равно / меньше или равно.

Текст запроса удобно собирать и отлаживать в Конструкторе запроса — он есть в контекстном меню модуля (пункт про конструктор запроса) и на закладке «Условия» формирует ГДЕ без ручного набора.

Одно выражение редко закрывает задачу, поэтому условия соединяют логическими связками — об этом дальше.

Параметры запроса вместо значений в тексте

Жёстко вписывать значения в текст запроса нельзя — ни число, ни строку, ни дату. Для этого есть параметры: имя с амперсандом внутри запроса и метод УстановитьПараметр снаружи.

Запрос = Новый Запрос;
Запрос.Текст =
    "ВЫБРАТЬ
    |    Контрагенты.Наименование
    |ИЗ
    |    Справочник.Контрагенты КАК Контрагенты
    |ГДЕ
    |    Контрагенты.ЮрФизЛицо = &ВидКонтрагента";
Запрос.УстановитьПараметр("ВидКонтрагента", ПредопределенноеЗначение("Перечисление.ЮрФизЛицо.ЮридическоеЛицо"));
Результат = Запрос.Выполнить();

Почему именно параметры, а не склейка строк:

  • 🔒 значение любого типа передаётся как есть — ссылка остаётся ссылкой, дата датой, не нужно форматировать;
  • ⚡ платформа кэширует разобранный текст запроса — при повторных вызовах с разными параметрами он не парсится заново;
  • 🛡️ исключается ошибка с кавычками и спецсимволами в пользовательском вводе.

Имя параметра в тексте (&ВидКонтрагента) и строка в УстановитьПараметр должны совпадать посимвольно — иначе параметр останется неустановленным, и платформа подставит NULL, из-за чего условие тихо вернёт пустую выборку.

Параметр может быть и списком значений (Новый СписокЗначений или массив) — тогда его используют вместе с оператором В, о котором ниже.

Связки И, ИЛИ, НЕ и скобки

Несколько условий объединяют связками И, ИЛИ, НЕ. У них разный приоритет: сначала вычисляется НЕ, затем И, в последнюю очередь ИЛИ. Из-за этого порядок без скобок часто даёт не тот результат, что ожидался.

Сравните два варианта:

// Без скобок: берёт проведённые по складу А плюс ВСЕ по складу Б
ГДЕ Док.Проведён И Док.Склад = &СкладА ИЛИ Док.Склад = &СкладБ

// Со скобками: проведённые по складу А или Б
ГДЕ Док.Проведён И (Док.Склад = &СкладА ИЛИ Док.Склад = &СкладБ)

Если в условии встречается хотя бы один ИЛИ, ставьте скобки вокруг него. Это снимает зависимость от приоритета операций и делает логику читаемой.

Оператор НЕ инвертирует условие: НЕ Док.Проведён оставит непроведённые. Но с НЕ есть тонкость — он плохо дружит с NULL: выражение НЕ (Поле = 5) не вернёт строки, где Поле равно NULL, потому что сравнение с NULL даёт не Ложь, а неопределённость. Этот случай разберём в разделе про пустые значения.

Проверка логики условия перед запуском
Выполнено: 0 / 5

Сравнение строк: ПОДОБНО и шаблоны

Точное совпадение строки задаётся через =, а поиск по части текста — оператором ПОДОБНО (аналог 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 для чужих типов.

Что замедляет отбор и как ускорить

Условие бывает правильным по результату, но тяжёлым по скорости. Разница на большой базе — секунды против минут. Основные причины и что делать:

  1. 🔎 Функция над полем. ВЫРАЗИТЬ(...), ГОД(Поле), ПОДОБНО "%...%" в левой части условия не дают использовать индекс. Сравнивайте само поле: вместо ГОД(Дата)=2024 — диапазон по Дата.
  2. 📋 Отбор по виртуальной таблице в ГДЕ. Для таблиц остатков и оборотов условие нужно задавать в параметрах виртуальной таблицы (, Склад = &Склад), а не в ГДЕ после выборки — тогда платформа отфильтрует данные на этапе чтения, а не после.
  3. 🚫 НЕ В и НЕ по большим спискам. Отрицание часто приводит к полному перебору таблицы. Где возможно, формулируйте условие через В без НЕ.
  4. 🔗 Отбор по полю через точку. ГДЕ Товар.Поставщик.Регион = &Регион — это неявное соединение. По возможности выносите такие условия в явные соединения или добавляйте реквизит в индекс.

Нажмите в консоли запросов или в технологическом журнале кнопку получения плана — он покажет, читается индекс или вся таблица. Если видите полное сканирование по большой таблице, ищите в условии функцию над полем.

Общее правило: условие должно сравнивать индексированное поле (Ссылка, Период, Регистратор, измерения регистров) напрямую с параметром или константой. Чем проще левая часть, тем выше шанс, что СУБД использует индекс.

Частые ошибки в условиях запроса

Ошибки, которые дают не синтаксический сбой, а тихо неверную выборку — их труднее всего заметить:

  • ❌ Сравнение ссылки со строкой. ГДЕ Контрагент = "ООО Ромашка" всегда ложно: слева ссылка, справа строка. Сравнивайте со ссылкой через параметр.
  • ❌ = NULL вместо ЕСТЬ NULL. ГДЕ Поле = NULL не вернёт ничего. Только ЕСТЬ NULL / ЕСТЬ НЕ NULL.
  • ❌ Потеря строк из-за NULL после соединения. Условие > 0 по полю из ЛЕВОГО СОЕДИНЕНИЯ отсекает несопоставленные строки. Оборачивайте в ЕСТЬNULL(Поле, 0).
  • ❌ Граница периода без конца дня. <= ДАТАВРЕМЯ(2024,1,31) теряет документы этого дня со временем. Берите КОНЕЦПЕРИОДА.
  • ❌ ИЛИ без скобок. Приоритет И выше ИЛИ, результат смещается. Скобки обязательны.
Проверьте себя
Какой оператор проверяет, что поле после соединения не содержит NULL?
Как захватить все документы за 31 января, включая вечерние?
Чем заменить длинную цепочку ИЛИ по одному полю?

Частые вопросы

Можно ли в условии сравнивать два поля одной таблицы?
Да. ГДЕ Товары.Количество > Товары.Зарезервировано — допустимое условие. Поля должны быть сравнимых типов.
Чем отличается В от В ИЕРАРХИИ?
В (...) ищет точное совпадение с перечисленными значениями. В ИЕРАРХИИ (...) дополнительно захватывает все элементы, лежащие внутри указанной группы иерархического справочника.
Почему условие с параметром вернуло пустую выборку, хотя данные есть?
Чаще всего имя параметра в тексте не совпало со строкой в УстановитьПараметр, и платформа подставила NULL. Сравнение с NULL ложно для любой строки — отбор пуст. Сверьте написание посимвольно.
Как отобрать строки с незаполненным реквизитом-ссылкой?
Сравните с пустой ссылкой нужного типа: ГДЕ Реквизит = ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка). Это именно пустая ссылка, а не NULL.
Нужно ли экранировать кавычки в значении для ПОДОБНО?
Само значение передавайте параметром — тогда кавычки и спецсимволы безопасны. Экранировать через СПЕЦСИМВОЛ нужно только символы шаблона % и _, если они должны искаться как обычные.