Условие связи ВЫБРАТЬ КОГДА в языке запросов 1С:Предприятие — один из самых мощных, но часто неправильно используемых инструментов. Оно позволяет объединять данные из нескольких таблиц не по стандартному равенству полей (как в СОЕДИНИТЬ), а по произвольному логическому выражению. Это открывает возможности для создания сложных отчетов, аналитики с нестандартными связями или обработки данных с разрывами в временных рядах.
Однако многие разработчики сталкиваются с проблемами: запрос работает медленно, возвращает неожиданные результаты или вообще не компилируется. В этой статье разберем синтаксис условия ВЫБРАТЬ КОГДА, покажем реальные примеры для типичных задач (связь по датам, интервалам, частичному совпадению), объясним как платформа 1С оптимизирует такие запросы под капотом и дадим рекомендации по ускорению их выполнения. Особое внимание уделим ловушкам, в которые попадают даже опытные программисты.
1. Синтаксис условия ВЫБРАТЬ КОГДА: основы и обязательные элементы
Конструкция ВЫБРАТЬ КОГДА в запросе 1С всегда используется внутри секции СОЕДИНИТЬ и заменяет стандартное условие равенства полей. Её общий вид:
ВЫБРАТЬ
ПоляИзЛевойТаблицы
ИЗ
ЛеваяТаблица КАК Л
ЛЕВОЕ СОЕДИНЕНИЕ ПраваяТаблица КАК П
ПО (ВЫБРАТЬ КОГДА Л.Поле1 > П.Поле2 И Л.Поле3 = П.Поле4)
Ключевые правила:
- 🔹 Условие обязательно заключается в скобки
(...)после ключевого словаПО. - 🔹 Внутри скобок используется полноценное логическое выражение (можно с
И,ИЛИ,НЕ). - 🔹 Поля в условии должны быть квалифицированы aliases'ами таблиц (например,
Л.Дата, а не простоДата). - 🔹 Можно использовать функции 1С (например,
НАЧАЛОПЕРИОДА(П.Дата, Месяц)).
Важно: условие ВЫБРАТЬ КОГДА работает только в контексте соединения таблиц. Его нельзя использовать в секциях ГДЕ или ИМЕЮЩИЕ — для этого есть другие конструкции.
2. Когда применять ВЫБРАТЬ КОГДА: 5 типичных сценариев
Стандартные соединения (ВНУТРЕННЕЕ, ЛЕВОЕ) покрывают 80% задач, но есть ситуации, где без ВЫБРАТЬ КОГДА не обойтись. Рассмотрим самые распространенные случаи:
2.1. Связь по неравенству (интервалы дат, диапазоны значений)
Классический пример — связать документы с регистром накопления по дате, когда дата документа попадает в период действия записи регистра:
ВЫБРАТЬ
Документ.Номер,
Документ.Дата,
Регистр.Количество
ИЗ
Документ Документ
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Остатки КАК Регистр
ПО (ВЫБРАТЬ КОГДА Документ.Дата >= Регистр.Период
И Документ.Дата < КОНЕЦДНЯ(Регистр.Период))
2.2. Связь по частичному совпадению (подстроки, шаблоны)
Например, связать номенклатуру с классификатором по первым 3 символам артикула:
ВЫБРАТЬ
Номенклатура.Наименование,
Классификатор.Группа
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Классификатор КАК Классификатор
ПО (ВЫБРАТЬ КОГДА ЛЕВ(Номенклатура.Артикул, 3) = Классификатор.Префикс)
2.3. Связь с учетом иерархии
Когда нужно связать элементы справочника не напрямую, а через родительские группы:
ВЫБРАТЬ
Деталь.Наименование,
Родитель.Наименование КАК Группа
ИЗ
Справочник.Номенклатура КАК Деталь
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Родитель
ПО (ВЫБРАТЬ КОГДА Деталь.Родитель = Родитель.Ссылка
ИЛИ Деталь.Родитель.Родитель = Родитель.Ссылка)
2.4. Связь по нескольким альтернативным условиям
Например, связать заказы с клиентами либо по основному контрагенту, либо по его филиалу:
ВЫБРАТЬ
Заказ.Номер,
Клиент.Наименование
ИЗ
Документ.ЗаказКлиента КАК Заказ
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Клиент
ПО (ВЫБРАТЬ КОГДА Заказ.Контрагент = Клиент.Ссылка
ИЛИ Заказ.Филиал = Клиент.Ссылка)
2.5. Связь с учетом статуса или флагов
Когда связь зависит от дополнительных условий, например, только для активных записей:
ВЫБРАТЬ
Заявка.Номер,
Исполнитель.Наименование
ИЗ
Документ.ЗаявкаНаОбслуживание КАК Заявка
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Сотрудники КАК Исполнитель
ПО (ВЫБРАТЬ КОГДА Заявка.Исполнитель = Исполнитель.Ссылка
И Исполнитель.Активен = ИСТИНА)
Если в условии связи используете функции (например, НАЧАЛОПЕРИОДА() или ЛЕВ()), добавьте их результаты в индексы таблиц базы данных — это ускорит выполнение запроса в 5-10 раз.
3. Оптимизация запросов с ВЫБРАТЬ КОГДА: как ускорить выполнение
Запросы с условием ВЫБРАТЬ КОГДА часто работают медленнее стандартных соединений, потому что:
- Платформа 1С не может использовать индексы так же эффективно, как при простом равенстве полей.
- Логические выражения в условии требуют вычислений для каждой пары строк.
- При большом объеме данных сервер СУБД может выбрать неоптимальный план выполнения.
Чтобы ускорить запрос, следуйте этим рекомендациям:
| Проблема | Решение | Пример |
|---|---|---|
Сложные функции в условии (например, НАЙТИ()) |
Перенесите вычисления в виртуальные таблицы или временные таблицы | |
Связь по датам с функциями (НАЧАЛОПЕРИОДА()) |
Используйте предвычисленные поля в запросе | |
Множество условий с ИЛИ |
Разбейте на несколько соединений с ОБЪЕДИНИТЬ |
|
Критически важно: если в условии связи используете поля, по которым нет индексов в СУБД, запрос будет выполняться перебором всех строк (full scan). Проверьте план выполнения запроса в консоли администрирования 1С!
Самый эффективный способ ускорить запрос с ВЫБРАТЬ КОГДА — максимально сузить выборку в секции ГДЕ до соединения. Чем меньше строк участвует в соединении, тем быстрее оно выполнится.
4. Типичные ошибки и как их избежать
Даже опытные разработчики допускают ошибки при работе с ВЫБРАТЬ КОГДА. Вот самые распространенные:
4.1. Использование неквалифицированных полей
Ошибка:
ПО (ВЫБРАТЬ КОГДА Дата > Период) // Неясно, из какой таблицы поля Дата и Период
Правильно:
ПО (ВЫБРАТЬ КОГДА Документ.Дата > Регистр.Период)
4.2. Забытые скобки вокруг условия
Ошибка:
ПО ВЫБРАТЬ КОГДА Л.Поле1 = П.Поле2 И Л.Поле3 > 100
Правильно:
ПО (ВЫБРАТЬ КОГДА Л.Поле1 = П.Поле2 И Л.Поле3 > 100)
4.3. Избыточные условия в ГДЕ и в соединении
Ошибка: дублирование одного и того же условия в ГДЕ и в ВЫБРАТЬ КОГДА. Это не только избыточно, но и может привести к конфликту оптимизатора.
4.4. Использование вложенных запросов в условии
Ошибка:
ПО (ВЫБРАТЬ КОГДА Л.Поле1 В (ВЫБРАТЬ П.Поле2 ИЗ ПраваяТаблица ГДЕ П.Поле3 = 100))
Правильно: сначала отфильтруйте данные во временной таблице, затем используйте её в основном запросе.
4.5. Неучет NULL-значений
Если поле в условии может быть NULL, явное сравнение (=, >) не сработает. Используйте ЕСТЬ NULL или ВЫРАЗИТЬ():
ПО (ВЫБРАТЬ КОГДА (Л.Поле1 ЕСТЬ NULL И П.Поле2 ЕСТЬ NULL)
ИЛИ Л.Поле1 = П.Поле2)
Что будет, если в условии связи использовать агрегатные функции?
Агрегатные функции (СУММА(), МАКСИМУМ()) в условии ВЫБРАТЬ КОГДА приводят к ошибке компиляции запроса. Для таких случаев нужно использовать подзапросы или временные таблицы с предварительно рассчитанными агрегатами.
5. Примеры реальных запросов с ВЫБРАТЬ КОГДА
Разберем практические задачи, где условие связи незаменимо.
5.1. Связь заказов с курсами валют на дату документа
Требуется показать сумму заказа в валюте с учетом курса на дату документа:
ВЫБРАТЬ
Заказ.Номер,
Заказ.Дата,
Заказ.Сумма / Курс.Курс КАК СуммаВРублях
ИЗ
Документ.ЗаказПокупателя КАК Заказ
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК Курс
ПО (ВЫБРАТЬ КОГДА Заказ.Дата >= Курс.Период
И Заказ.Дата < КОНЕЦДНЯ(Курс.Период)
И Заказ.Валюта = Курс.Валюта)
ГДЕ
Заказ.Валюта <> ЗНАЧЕНИЕ(Перечисление.Валюты.Рубль)
5.2. Связь товаров с акциями по периоду действия
Нужно показать товары с действующими на них скидками:
ВЫБРАТЬ
Товар.Наименование,
Товар.Цена,
Акция.ПроцентСкидки,
Товар.Цена * (1 - Акция.ПроцентСкидки/100) КАК ЦенаСоСкидкой
ИЗ
Справочник.Номенклатура КАК Товар
ЛЕВОЕ СОЕДИНЕНИЕ Документ.Акции КАК Акция
ПО (ВЫБРАТЬ КОГДА ТЕКУЩАЯДАТА() >= Акция.ДатаНачала
И ТЕКУЩАЯДАТА() <= Акция.ДатаОкончания
И Товар.Группа В (ВЫБРАТЬ Акция.ГруппыТоваров.Группа))
ГДЕ
Акция.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыАкций.Активна)
5.3. Связь сотрудников с графиками работы
Отчет по сотрудникам с указанием их графика на текущую дату:
ВЫБРАТЬ
Сотрудник.Наименование,
График.Наименование КАК ГрафикРаботы,
График.НачалоРабочегоДня,
График.КонецРабочегоДня
ИЗ
Справочник.Сотрудники КАК Сотрудник
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ГрафикиРаботыСотрудников КАК График
ПО (ВЫБРАТЬ КОГДА ТЕКУЩАЯДАТА() >= График.Период
И ТЕКУЩАЯДАТА() < КОНЕЦДНЯ(График.Период)
И Сотрудник.Ссылка = График.Сотрудник)
Все поля в условии квалифицированы aliases'ами таблиц|Условие заключено в скобки (...)|Нет дублирования условий в ГДЕ и в соединении|Для дат используются функции начала/конца периода|Поля в условии проиндексированы в СУБД-->
6. Альтернативы ВЫБРАТЬ КОГДА: когда лучше использовать другие подходы
Условие ВЫБРАТЬ КОГДА не всегда оптимально. Рассмотрим альтернативы:
6.1. Виртуальные таблицы
Если нужно связать данные по сложному условию, иногда проще предварительно подготовить данные во временной таблице:
ВТ_Связи = НОВЫЙ ТаблицаЗначений;
ВТ_Связи.Колонки.Добавить("Документ");
ВТ_Связи.Колонки.Добавить("Регистр");
// Заполняем связи заранее
ВЫБРАТЬ
Документ.Номер,
Регистр.Количество
ИЗ
Документ КАК Документ
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Связи КАК Связи ПО Документ.Ссылка = Связи.Документ
ЛЕВОЕ СОЕДИНЕНИЕ Регистр КАК Регистр ПО Регистр.Ссылка = Связи.Регистр
6.2. Подзапросы в секции ГДЕ
Для простых условий иногда удобнее использовать В() или СУЩЕСТВУЕТ():
ВЫБРАТЬ
Документ.Номер
ИЗ
Документ КАК Документ
ГДЕ
СУЩЕСТВУЕТ(
ВЫБРАТЬ РАЗРЕШЕННЫЕ 1
ИЗ Регистр КАК Регистр
ГДЕ Регистр.Период = НАЧАЛОПЕРИОДА(Документ.Дата, Месяц)
)
6.3. Объединение нескольких простых запросов
Если условие связи слишком сложное, разбейте его на части с ОБЪЕДИНИТЬ:
ВЫБРАТЬ ... ПО (Условие1)
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ ... ПО (Условие2)
Если запрос с ВЫБРАТЬ КОГДА выполняется дольше 5 секунд, скорее всего, стоит рассмотреть альтернативные подходы. Начните с анализа плана выполнения в консоли 1С!
7. Особенности работы в разных версиях 1С
Поведение условия ВЫБРАТЬ КОГДА может отличаться в зависимости от версии платформы и СУБД:
| Версия 1С / СУБД | Особенности | Рекомендации |
|---|---|---|
| 1С 8.2, MS SQL | Ограничение на сложность условий (не более 3-4 вложенных функций) | Разбивайте сложные условия на подзапросы |
| 1С 8.3.6+, PostgreSQL | Поддержка оконных функций в условиях | Можно использовать РАЗДЕЛИТЬ ПО() для аналитики |
| 1С 8.3.10+, любая СУБД | Оптимизатор лучше работает с виртуальными таблицами | Предпочитайте временные таблицы для сложных связей |
| 1С:ERP, Oracle | Поддержка аналитических функций в условиях | Используйте LAG()/LEAD() для сравнения с предыдущими записями |
Для версий 1С ниже 8.3.8 условие ВЫБРАТЬ КОГДА с функциями дат (НАЧАЛОПЕРИОДА(), КОНЕЦПЕРИОДА()) может работать некорректно при переходе на летнее/зимнее время. В таких случаях используйте явное приведение к дате без времени:
ПО (ВЫБРАТЬ КОГДА НАЧАЛОДНЯ(Документ.Дата) >= НАЧАЛОДНЯ(Регистр.Период))
Как проверить, поддерживает ли ваша версия 1С сложные условия в ВЫБРАТЬ КОГДА?
Создайте тестовый запрос с условием, содержащим 5-6 вложенных функций (например, ЕСТЬNULL(НАЧАЛОПЕРИОДА(ДобавитьМесяц(Дата, 1), Квартал), Дата)). Если запрос компилируется без ошибок — ваша версия поддерживает сложные условия.
8. Практические советы по отладке
Отладка запросов с ВЫБРАТЬ КОГДА может быть сложной. Вот проверенные приемы:
8.1. Просмотр плана выполнения
В консоли запросов 1С (Отладка → План выполнения) обратите внимание на:
- 🔍 Операции
Nested Loops— признак неоптимального соединения. - 🔍
Table Scan— означает, что не используются индексы. - 🔍 Оценку количества строк (
Estimated Rows) — если она сильно отличается от реальной, обновляйте статистику СУБД.
8.2. Поэтапное усложнение запроса
Начинайте с минимального рабочего примера, затем добавляйте условия:
// Шаг 1: только соединение без условий
ВЫБРАТЬ Д.Ссылка, Р.Ссылка
ИЗ Документ КАК Д ЛЕВОЕ СОЕДИНЕНИЕ Регистр КАК Р ПО 1=1
// Шаг 2: добавляем простое условие
ПО (ВЫБРАТЬ КОГДА Д.Дата = Р.Период)
// Шаг 3: усложняем условие
ПО (ВЫБРАТЬ КОГДА Д.Дата >= Р.Период И Д.Дата < КОНЕЦДНЯ(Р.Период))
8.3. Логирование промежуточных результатов
Для сложных запросов сохраняйте промежуточные данные во временные таблицы и проверяйте их содержимое:
ВТ_ПромежуточныйРезультат = НОВЫЙ ТаблицаЗначений;
// Заполняем через запрос с частью условий
ВЫБРАТЬ ... ПОМЕСТИТЬ ВТ_ПромежуточныйРезультат;
// Проверяем данные
Сообщить(ВТ_ПромежуточныйРезультат.Количество());
8.4. Использование конструктора запросов
В режиме 1С:Предприятие конструктор запросов визуализирует связи между таблицами, что помогает обнаружить ошибки в условиях.
Если запрос с ВЫБРАТЬ КОГДА возвращает пустой результат, сначала проверьте, что обе таблицы содержат данные отдельными запросами. Часто проблема не в условии связи, а в фильтрах в секции ГДЕ.
⚠️ Внимание: В некоторых конфигурациях (например, 1С:ERP или 1С:УТ) стандартные регистры и документы имеют предопределенные индексы. Изменение структуры этих объектов может привести к поломке типовой функциональности. Перед добавлением новых индексов для оптимизации запросов сделайте резервную копию базы данных.
FAQ: Ответы на частые вопросы
Можно ли использовать ВЫБРАТЬ КОГДА для соединения более двух таблиц?
Да, но с осторожностью. При соединении трех и более таблиц с условиями ВЫБРАТЬ КОГДА оптимизатор 1С может выбрать неэффективный план выполнения. В таких случаях:
- Сначала соединяйте таблицы с простыми условиями (
=). - Затем добавляйте таблицы с условием
ВЫБРАТЬ КОГДА. - Используйте временные таблицы для промежуточных результатов.
Пример:
ВЫБРАТЬ ...
ИЗ Таблица1 КАК Т1
ЛЕВОЕ СОЕДИНЕНИЕ Таблица2 КАК Т2 ПО Т1.Поле = Т2.Поле
ЛЕВОЕ СОЕДИНЕНИЕ Таблица3 КАК Т3
ПО (ВЫБРАТЬ КОГДА Т1.Дата >= Т3.Период И Т2.Статус = Т3.Статус)
Почему запрос с ВЫБРАТЬ КОГДА работает медленно, хотя данных мало?
Причины могут быть следующими:
- 🐢 Отсутствуют индексы на поля, используемые в условии связи. Проверьте в конфигураторе (
Все функции → Индексы). - 🐢 В условии используются функции, которые не могут использовать индексы (например,
НАЙТИ(),СОКРЛП()). - 🐢 Сервер СУБД выбрал неоптимальный план. Попробуйте использовать подсказки оптимизатору (например,
/+ INDEX(Таблица Индекс) /для Oracle). - 🐢 В временных таблицах не обновлена статистика. Выполните
ОБНОВИТЬ СТАТИСТИКУдля базы данных.
Для диагностики:
- Посмотрите план выполнения запроса.
- Проверьте, какие индексы используются (или не используются).
- Упростите запрос, удаляя условия по одному, чтобы найти "узкое место".
Как связать таблицы по условию "последняя запись на дату"?
Это классическая задача для ВЫБРАТЬ КОГДА. Используйте конструкцию:
ВЫБРАТЬ
Документ.Номер,
Документ.Дата,
ПоследняяЦ