Работа с отборами в запросах 1С:Предприятие — одна из самых востребованных задач среди разработчиков и аналитиков. Без грамотного применения условий отбора даже простой запрос может возвращать тысячи ненужных строк, замедлять систему или выдавать некорректные данные. В этой статье мы разберём не только базовый синтаксис, но и скрытые возможности языка запросов, которые позволят вам писать код эффективнее.
Отборы в 1С используются везде: от формирования отчётов до сложных аналитических выборок. Однако многие разработчики ограничиваются примитивными конструкциями вроде ГДЕ Номенклатура = &Номенклатура, не подозревая, что язык запросов поддерживает регулярные выражения, вложенные условия и даже динамические параметры с проверкой типов. Мы покажем, как избежать типичных ошибок (например, сравнения дат в неверном формате) и ускорить выполнение запросов за счёт правильной индексации.
Особое внимание уделим отборам по виртуальным таблицам — это одна из самых сложных тем, где ошибки приводят к непредсказуемым результатам. Вы узнаете, почему иногда условие ГДЕ Период МЕЖДУ &Начало И &Конец работает не так, как ожидается, и как это исправить.
1. Базовый синтаксис отборов в запросах 1С
Любой отбор в запросе 1С начинается с ключевого слова ГДЕ (или WHERE в английской нотации). После него следуют условия, соединённые логическими операторами И, ИЛИ, НЕ. Например:
ВЫБРАТЬ
Номенклатура.Наименование,
Номенклатура.Артикул
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.ПометкаУдаления = ЛОЖЬ
И Номенклатура.ЭтоГруппа = ЛОЖЬ
Если первое условие в отборе выполняется дольше остальных, запрос будет тормозить. Например, проверка Номенклатура.Артикул НЕ ПУСТАЯ СТРОКА работает быстрее, чем Номенклатура.Родитель = &Родитель, если поле Артикул проиндексировано.
- 📌 Равенство:
=,<>(не равно). Пример:ГДЕ Статус = &Статус - 📅 Даты:
МЕЖДУ,>=,<=. Пример:ГДЕ Дата МЕЖДУ &Начало И &Конец - 🔍 Поиск по строке:
СОДЕРЖИТ,НАЧИНАЕТСЯ. Пример:ГДЕ Наименование СОДЕРЖИТ "Тест" - 📊 Null-значения:
ЕСТЬ NULL,ЗНАЧЕНИЕ ЗАПОЛНЕНО. Пример:ГДЕ Контрагент ЕСТЬ NULL
⚠️ Внимание: При сравнении дат всегда используйте параметры (&Дата), а не жёстко прописанные значения. Иначе запрос не будет использовать индексы, и его выполнение замедлится в 10–100 раз.
2. Сложные условия: И, ИЛИ, НЕ и скобки
Когда требуется комбинировать несколько условий, важно правильно расставлять скобки. Без них логика отбора может исказиться. Например:
ГДЕ
(Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыДокументов.Проведен)
ИЛИ Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыДокументов.Оплачен))
И Дата > &МинимальнаяДата
Здесь скобки гарантируют, что сначала проверяется статус, а потом — дата. Без них условие сработает как (Статус = Проведен ИЛИ Дата > &МинимальнаяДата), что приведёт к выборке лишних данных.
Оператор НЕ часто вызывает путаницу. Например, ГДЕ НЕ (Статус = Проведен) и ГДЕ Статус <> Проведен — не одно и то же! Первое условие исключает только значение "Проведен", а второе — все документы с любым другим статусом, включая NULL.
- 🔄 Приоритет операторов:
НЕ→И→ИЛИ. Всегда используйте скобки для ясности. - 🚫 Отрицание:
НЕ (А ИЛИ Б)эквивалентно(НЕ А) И (НЕ Б)(закон де Моргана). - 📈 Производительность: Условия с
Ивыполняются быстрее, чем сИЛИ, если они стоят первыми.
3. Отборы по датам и периодам
Работа с датами — одна из самых частых задач в 1С. Ошибки здесь приводят к пропуску записей или, наоборот, к выборке лишних данных. Например, условие ГДЕ Дата = &ТекущаяДата не учтёт время, если поле Дата имеет тип ДатаВремя. Правильнее писать:
ГДЕ Дата >= НАЧАЛОДНЯ(&ТекущаяДата)
И Дата < КОНЕЦДНЯ(&ТекущаяДата) + 1
Для работы с периодами используйте МЕЖДУ, но помните: это условие включает граничные значения. Если нужно исключить крайние даты, пишите явно:
ГДЕ Дата > &Начало
И Дата < &Конец
| Задача | Неверный отбор | Правильный отбор |
|---|---|---|
| Выборка за текущий день | Дата = ТЕКУЩАЯДАТА() |
Дата >= НАЧАЛОДНЯ(ТЕКУЩАЯДАТА()) |
| Выборка за месяц | Дата МЕЖДУ НачалоМесяца(Дата) И КонецМесяца(Дата) |
Дата >= НачалоМесяца(&Дата) И Дата <= КонецМесяца(&Дата) |
| Исключение выходных | ДЕНЬНЕДЕЛИ(Дата) <> 6 И ДЕНЬНЕДЕЛИ(Дата) <> 7 |
НЕ (ДЕНЬНЕДЕЛИ(Дата) В (6, 7)) |
⚠️ Внимание: Функции вродеНАЧАЛОДНЯ()илиДЕНЬНЕДЕЛИ()нельзя применять к полям в условиях отбора — это блокирует использование индексов. Всегда вычисляйте такие значения заранее и передавайте как параметры.
4. Отборы по виртуальным таблицам
Виртуальные таблицы (например, Документ.РеализацияТоваровУслуг.Обороты) требуют особого подхода. Здесь нельзя использовать стандартные условия для полей, которые не существуют в физической таблице. Например, такой запрос вызовет ошибку:
ВЫБРАТЬ
Обороты.Номенклатура,
Обороты.Количество
ИЗ
Документ.РеализацияТоваровУслуг.Обороты(&Начало, &Конец,) КАК Обороты
ГДЕ
Обороты.Сумма > 1000
Проблема в том, что поле Сумма не хранится в таблице оборотов — оно вычисляется динамически. Правильный подход:
ВЫБРАТЬ
Обороты.Номенклатура,
Обороты.Количество
ИЗ
Документ.РеализацияТоваровУслуг.Обороты(&Начало, &Конец,) КАК Обороты
ГДЕ
Обороты.Количество * Обороты.Цена > 1000
Для виртуальных таблиц обязательно указывайте параметры периодов, даже если они не используются в отборе. Иначе платформа не сможет оптимально построить запрос.
Указаны все обязательные параметры (период, измерения)|
Поля в условии существуют в физической таблице|
Используются индексируемые поля для отбора|
Проверена логика вычисляемых полей-->
5. Динамические отборы и параметры
В реальных задачах условия отбора часто формируются динамически — например, в зависимости от прав пользователя или настроек отчёта. Для этого используйте динамические параметры и конструкцию ЕСЛИ.. ТОГДА.. ИНАЧЕ:
Запрос.Текст =
"ВЫБРАТЬ
Номенклатура.Наименование
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
" + ?(НужноТолькоАктивные, "Номенклатура.ПометкаУдаления = ЛОЖЬ", "") +
?(ФильтрПоГруппе <> Неопределено, " И Номенклатура.Родитель = &Родитель", "");
Важно: при динамическом формировании текста запроса всегда экранируйте параметры, чтобы избежать SQL-инъекций. В 1С для этого используйте метод ЭкранироватьСтроку():
Параметр = ЭкранироватьСтроку(ПользовательскийВвод, Ложь);
Запрос.УстановитьПараметр("Поиск", "%" + Параметр + "%");
Для сложных динамических отборов удобно использовать временные таблицы. Например, если нужно отфильтровать номенклатуру по списку артикулов:
// Создаём временную таблицу с артикулами
ВТ_Артикулы = Новый ТаблицаЗначений;
ВТ_Артикулы.Колонки.Добавить("Артикул");
// Заполняем данными
Для Каждого Артикул Из СписокАртикулов Цикл
Строка = ВТ_Артикулы.Добавить();
Строка.Артикул = Артикул;
КонецЦикла;
// Используем во временной таблице запроса
Запрос.Текст =
"ВЫБРАТЬ
Номенклатура.Наименование
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.Артикул В (&ВТ_Артикулы)";
Запрос.УстановитьПараметр("ВТ_Артикулы", ВТ_Артикулы);
Если динамический отбор становится слишком сложным, разбейте его на несколько запросов с промежуточными временными таблицами. Это упростит отладку и ускорит выполнение.
6. Оптимизация отборов: индексы и планы выполнения
Даже правильно написанный отбор может работать медленно, если не учитывать индексы. В 1С индексы создаются автоматически для полей, отмеченных в конфигураторе как индексируемые, но их можно добавлять и вручную через ИНДЕКСИРОВАТЬ ПО:
ВЫБРАТЬ
Документ.ЗаказПокупателя.Номер КАК Номер
ИЗ
Документ.ЗаказПокупателя КАК ЗаказПокупателя
ИНДЕКСИРОВАТЬ ПО
Дата,
Контрагент
ГДЕ
ЗаказПокупателя.Дата МЕЖДУ &Начало И &Конец
И ЗаказПокупателя.Контрагент = &Контрагент
Чтобы проверить, использует ли запрос индексы, включите план выполнения:
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ..";
Запрос.ПланВопроса = Истина; // Включаем вывод плана
Результат = Запрос.Выполнить();
В плане обратите внимание на:
- 🔍 Полное сканирование (
Seq Scan) — означает, что индексы не используются. - ⚡ Индексное сканирование (
Index Scan) — оптимальный вариант. - 📉 Стоимость операции (
Cost) — чем выше, тем медленнее запрос.
⚠️ Внимание: Если в условии отбора используется функция (например,НАЧИНАЕТСЯ(Наименование, "А")), индекс по полюНаименованиеиспользоваться не будет. Переносите такие проверки в секциюГДЕчерез параметры или используйте временные таблицы.
Индексы ускоряют отборы только если условия написаны в формате "поле = значение" или "поле > значение". Функции, вычисления и OR блокируют использование индексов.
7. Типичные ошибки и как их избежать
Даже опытные разработчики допускают ошибки при работе с отборами. Вот самые распространённые:
- Сравнение разных типов. Например,
ГДЕ Код = "123", гдеКод— число, а"123"— строка. Это приведёт к неявному приведению типов и медленному выполнению. Всегда следите за типами параметров: - Игнорирование NULL. Условие
ГДЕ Поле <> Значениене вернёт строки, гдеПоле = NULL. ИспользуйтеГДЕ Поле <> Значение ИЛИ Поле ЕСТЬ NULL. - Неправильные даты. Условие
ГДЕ Дата = &Датане сработает для поля типаДатаВремя, так как время будет учитываться. ИспользуйтеМЕЖДУ НАЧАЛОДНЯ() И КОНЕЦДНЯ(). - Избыточные условия. Например,
ГДЕ (А И Б) ИЛИ (А И В)можно упростить доГДЕ А И (Б ИЛИ В), что ускорит выполнение.
Запрос.УстановитьПараметр("Код", Число(123)); // Правильно
Ещё одна частая проблема — неявные соединения в условиях. Например:
ВЫБРАТЬ
Заказ.Номер
ИЗ
Документ.ЗаказПокупателя КАК Заказ,
Справочник.Контрагенты КАК Контрагент
ГДЕ
Заказ.Контрагент = Контрагент.Ссылка
И Контрагент.Наименование СОДЕРЖИТ "ООО"
Такой запрос создаёт CROSS JOIN (декартово произведение), что крайне неэффективно. Замените его на явное соединение:
ВЫБРАТЬ
Заказ.Номер
ИЗ
Документ.ЗаказПокупателя КАК Заказ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагент
ПО Заказ.Контрагент = Контрагент.Ссылка
ГДЕ
Контрагент.Наименование СОДЕРЖИТ "ООО"
8. Продвинутые техники: регулярные выражения и полнотекстовый поиск
Для сложных строковых отборов в 1С 8.3.14+ доступны регулярные выражения через функцию РЕГВЫРАЖ(). Например, чтобы найти артикулы в формате ABC-123:
ГДЕ РЕГВЫРАЖ(Артикул, "^[A-Z]{3}-\d{3}$", Истина) = Истина
Параметры функции:
^— начало строки,[A-Z]{3}— ровно 3 заглавные буквы,-\d{3}$— тире и 3 цифры до конца строки.
Для полнотекстового поиска (например, поиска по словам с учётом морфологии) используйте оператор ПОДОБНО или специализированные функции:
ГДЕ ПОЛНЫЙТЕКСТ(Наименование, "телефон смартфон")
Это особенно полезно для справочников с большим количеством записей, где стандартный СОДЕРЖИТ работает слишком медленно.
Как ускорить полнотекстовый поиск?
Для ускорения полнотекстового поиска в 1С:
1. Настройте полнотекстовые индексы в конфигураторе (раздел "Полнотекстовый поиск").
2. Используйте предварительную фильтрацию по другим полям (например, по группе номенклатуры).
3. Ограничивайте количество возвращаемых строк с помощью ПЕРВЫЕ 100.
FAQ: Ответы на частые вопросы по отборам в 1С
1. Почему мой запрос игнорирует условие отбора?
Чаще всего это происходит из-за:
- Несовпадения типов (например, сравнение числа со строкой).
- Ошибок в логике условий (пропущенные скобки или операторы).
- Использования функций в отборе (например,
ЛЕВ(Наименование, 3) = "АБВ"блокирует индексы).
Проверьте план выполнения запроса, чтобы увидеть, как именно работает ваш отбор.
2. Как сделать отбор по нескольким значениям (например, список номенклатуры)?
Используйте оператор В с массивом или временной таблицей:
ГДЕ Номенклатура В (&МассивНоменклатуры)
Для больших списков (более 1000 элементов) лучше использовать временную таблицу.
3. Можно ли в отборе использовать подзапросы?
Да, но осторожно. Подзапросы в условиях ГДЕ могут сильно замедлить выполнение. Пример:
ГДЕ Контрагент В (
ВЫБРАТЬ Контрагент
ИЗ Документ.ЗаказПокупателя
ГДЕ Дата > &МинимальнаяДата
)
Лучше заменить такой подзапрос на соединение или временную таблицу.
4. Как отладить сложный отбор?
Используйте следующие техники:
- Выводите план выполнения (
Запрос.ПланВопроса = Истина). - Разбивайте запрос на части и проверяйте каждую отдельно.
- Используйте
Сообщить()для вывода промежуточных результатов.
5. Почему отбор по виртуальной таблице возвращает неверные данные?
Виртуальные таблицы (например, обороты или остатки) зависят от переданных параметров. Типичные ошибки:
- Не указан период (
Обороты(&Начало, &Конец)). - Неверный порядок параметров (например, перепутаны начало и конец периода).
- Отбор по вычисляемым полям (например,
СуммаНДС), которые не существуют в физической таблице.
Всегда сверяйтесь с документацией по конкретной виртуальной таблице.