При разработке отчетов и обработок разработчики 1С часто сталкиваются с задачей, когда требуется отфильтровать записи одной таблицы на основе агрегатного значения из другой таблицы (например, отбирать данные, которые больше максимальной даты или максимального значения суммы). Попытка написать конструкцию вида ГДЕ Таб1.Значение3 > (ВЫБРАТЬ МАКСИМУМ(...) ИЗ ...) приводит к синтаксической ошибке платформы.
Рассмотрим подробнее причины возникновения этой ошибки и разберем эффективные способы решения данной задачи на практических примерах.
В языке запросов платформы 1С Предприятие существует жесткое ограничение: в секции ГДЕ использование подзапросов допускается только в сочетании с операторами В (проверка вхождения в список/выборку) или В ИЕРАРХИИ. Использовать скалярные операторы сравнения (>, <, =, <>) вместе со скобками вложенного выборки в условии ГДЕ недопустимо. Платформа выдаст синтаксическую ошибку при попытке вызова метода Выполнить().
Проанализируем доступные варианты, которые позволяют корректно реализовать эту логику.
Самый быстрый способ обойти ограничение — поместить запрос, рассчитывающий агрегатное значение (например, МАКСИМУМ), во вложенный запрос и соединить его с основной таблицей по условию сравнения.
Рассмотрим пример запроса:
ВЫБРАТЬ
Т1.Значение1 КАК Значение1,
Т1.Значение2 КАК Значение2
ИЗ
Таб1 КАК Т1
ЛЕВОЕ СОЕДИНЕНИЕ (
ВЫБРАТЬ
МАКСИМУМ(Таб2.Значение) КАК МаксЗначение
ИЗ
Таб2 КАК Таб2
) КАК Т2
ПО Т1.Значение3 > Т2.МаксЗначение
В данном случае платформа вычисляет максимум из таблицы Таб2 и связывает каждую строку Таб1 с полученным значением по условию Т1.Значение3 > Т2.МаксЗначение.
Использование вложенных запросов непосредственно в условии соединения (как в Способе 1) может негативно сказываться на производительности при больших объемах данных, так как оптимизаторы СУБД не всегда могут построить оптимальный план выполнения.
Стандартом разработки 1С является использование пакета запросов и временных таблиц с индексацией. Разберем этот вариант по шагам:
Таб2Макс.Посмотрим на код запроса:
ВЫБРАТЬ
МАКСИМУМ(Таб2.Значение) КАК Значение
ПОМЕСТИТЬ Таб2Макс
ИЗ
Таб2 КАК Таб2
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Таб1.Значение1 КАК Значение1,
Таб1.Значение2 КАК Значение2
ИЗ
Таб1 КАК Таб1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Таб2Макс КАК Таб2Макс
ПО Таб1.Значение3 > Таб2Макс.Значение
Этот подход обеспечивает высокую скорость выполнения даже на крупных информационных базах и считается наиболее правильным с точки зрения стандартов разработки 1С.
Если условие фильтрации применяется к выборке, которую неудобно переписывать через соединения (например, в сложных отчетах со множеством объединений или при использовании построителя отчетов), можно вычислить требуемое максимальное значение отдельным легким запросом во встроенном языке, а затем передать его как параметр в основной запрос.
Рассмотрим пример на встроенном языке 1С:
ЗапросМакс = Новый Запрос;
ЗапросМакс.Текст =
"ВЫБРАТЬ
| МАКСИМУМ(Таб2.Значение) КАК Максимум
|ИЗ
| Таб2 КАК Таб2";
Выборка = ЗапросМакс.Выполнить().Выбрать();
МаксЗначение = 0;
Если Выборка.Следующий() Тогда
МаксЗначение = Выборка.Максимум;
КонецЕсли;
ЗапросОсновной = Новый Запрос;
ЗапросОсновной.Текст =
"ВЫБРАТЬ
| Таб1.Значение1 КАК Значение1,
| Таб1.Значение2 КАК Значение2
|ИЗ
| Таб1 КАК Таб1
|ГДЕ
| Таб1.Значение3 > &МаксЗначение";
ЗапросОсновной.УстановитьПараметр("МаксЗначение", МаксЗначение);
Результат = ЗапросОсновной.Выполнить().Выгрузить();
Важное замечание: При работе с левыми соединениями и параметрами всегда учитывайте возможность получения значения NULL. В запросах рекомендуем оборачивать получаемые значения в функцию ЕСТЬNULL(), чтобы избежать некорректных результатов при сравнении данных.