Как в запросе 1С сравнить значение поля с результатом выборки или подзапроса

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При разработке отчетов и обработок разработчики 1С часто сталкиваются с задачей, когда требуется отфильтровать записи одной таблицы на основе агрегатного значения из другой таблицы (например, отбирать данные, которые больше максимальной даты или максимального значения суммы). Попытка написать конструкцию вида ГДЕ Таб1.Значение3 > (ВЫБРАТЬ МАКСИМУМ(...) ИЗ ...) приводит к синтаксической ошибке платформы.

Рассмотрим подробнее причины возникновения этой ошибки и разберем эффективные способы решения данной задачи на практических примерах.

Выясним причину ошибки синтаксиса

В языке запросов платформы 1С Предприятие существует жесткое ограничение: в секции ГДЕ использование подзапросов допускается только в сочетании с операторами В (проверка вхождения в список/выборку) или В ИЕРАРХИИ. Использовать скалярные операторы сравнения (>, <, =, <>) вместе со скобками вложенного выборки в условии ГДЕ недопустимо. Платформа выдаст синтаксическую ошибку при попытке вызова метода Выполнить().

Проанализируем доступные варианты, которые позволяют корректно реализовать эту логику.

Способ 1. Соединение с подзапросом в секции ИЗ

Самый быстрый способ обойти ограничение — поместить запрос, рассчитывающий агрегатное значение (например, МАКСИМУМ), во вложенный запрос и соединить его с основной таблицей по условию сравнения.

Рассмотрим пример запроса:


ВЫБРАТЬ
	Т1.Значение1 КАК Значение1,
	Т1.Значение2 КАК Значение2
ИЗ
	Таб1 КАК Т1
		ЛЕВОЕ СОЕДИНЕНИЕ (
			ВЫБРАТЬ
				МАКСИМУМ(Таб2.Значение) КАК МаксЗначение
			ИЗ
				Таб2 КАК Таб2
		) КАК Т2
		ПО Т1.Значение3 > Т2.МаксЗначение

В данном случае платформа вычисляет максимум из таблицы Таб2 и связывает каждую строку Таб1 с полученным значением по условию Т1.Значение3 > Т2.МаксЗначение.

Способ 2. Использование пакета запросов и временных таблиц (Рекомендуемый подход)

Использование вложенных запросов непосредственно в условии соединения (как в Способе 1) может негативно сказываться на производительности при больших объемах данных, так как оптимизаторы СУБД не всегда могут построить оптимальный план выполнения.

Стандартом разработки 1С является использование пакета запросов и временных таблиц с индексацией. Разберем этот вариант по шагам:

  1. Первым запросом пакета вычисляем максимальное значение и сохраняем его во временную таблицу Таб2Макс.
  2. Вторым запросом соединяем основную таблицу с созданной временной таблицей.

Посмотрим на код запроса:


ВЫБРАТЬ
	МАКСИМУМ(Таб2.Значение) КАК Значение
ПОМЕСТИТЬ Таб2Макс
ИЗ
	Таб2 КАК Таб2
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	Таб1.Значение1 КАК Значение1,
	Таб1.Значение2 КАК Значение2
ИЗ
	Таб1 КАК Таб1
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Таб2Макс КАК Таб2Макс
		ПО Таб1.Значение3 > Таб2Макс.Значение

Этот подход обеспечивает высокую скорость выполнения даже на крупных информационных базах и считается наиболее правильным с точки зрения стандартов разработки 1С.

Способ 3. Предварительный расчет значения и передача через параметр

Если условие фильтрации применяется к выборке, которую неудобно переписывать через соединения (например, в сложных отчетах со множеством объединений или при использовании построителя отчетов), можно вычислить требуемое максимальное значение отдельным легким запросом во встроенном языке, а затем передать его как параметр в основной запрос.

Рассмотрим пример на встроенном языке 1С:


ЗапросМакс = Новый Запрос;
ЗапросМакс.Текст = 
	"ВЫБРАТЬ
	|	МАКСИМУМ(Таб2.Значение) КАК Максимум
	|ИЗ
	|	Таб2 КАК Таб2";

Выборка = ЗапросМакс.Выполнить().Выбрать();
МаксЗначение = 0;
Если Выборка.Следующий() Тогда
	МаксЗначение = Выборка.Максимум;
КонецЕсли;

ЗапросОсновной = Новый Запрос;
ЗапросОсновной.Текст = 
	"ВЫБРАТЬ
	|	Таб1.Значение1 КАК Значение1,
	|	Таб1.Значение2 КАК Значение2
	|ИЗ
	|	Таб1 КАК Таб1
	|ГДЕ
	|	Таб1.Значение3 > &МаксЗначение";

ЗапросОсновной.УстановитьПараметр("МаксЗначение", МаксЗначение);
Результат = ЗапросОсновной.Выполнить().Выгрузить();

Важное замечание: При работе с левыми соединениями и параметрами всегда учитывайте возможность получения значения NULL. В запросах рекомендуем оборачивать получаемые значения в функцию ЕСТЬNULL(), чтобы избежать некорректных результатов при сравнении данных.

← На главную