Циклическая вложенная фильтрация

29.09.25

Разработка - Запросы

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

 

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

 

Процедура Пример3НаСервере()

	БазовыйЗапросТекст =
		"ВЫБРАТЬ
		|	Пользователи.Ссылка КАК Ссылка
		|ИЗ
		|	Справочник.Пользователи КАК Пользователи";

	БазовыйЗапрос = Новый Запрос(БазовыйЗапросТекст);
	
	// Инициализируем редактор запросов
	РедакторЗапроса = Обработки.сп_РедакторЗапросов.Инициализировать(БазовыйЗапрос);
	
	// Выполним пакетную фильтрацию
	ПакетнаяФильтрацияОтборыПрименить(РедакторЗапроса, "СрокДействия", ">", Дата(2025, 1, 1));
	ПакетнаяФильтрацияОтборыПрименить(РедакторЗапроса, "АвторФайла", "=", "СсылкаНаФайл");
	ПакетнаяФильтрацияОтборыПрименить(РедакторЗапроса, "АвторПроцессов", "=", "СсылкаНаПроцесс");

	Выборка = РедакторЗапроса.СкомпоноватьИВыбрать();
	Пока Выборка.Следующий() Цикл

	КонецЦикла;

КонецПроцедуры

Процедура ПакетнаяФильтрацияОтборыПрименить(РедакторЗапроса, ФильтрПоле, ФильтрУсловиеСравения, ФильтрЗначение)

	Если ФильтрПоле = "СрокДействия" Тогда

		ПодзапросТекст = 
			"ВЫБРАТЬ
			|	БазовыйЗапрос.*
			|ИЗ
			|	БазовыйЗапрос КАК БазовыйЗапрос
			|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СведенияОПользователях КАК СведенияОПользователях
			|		ПО БазовыйЗапрос.Ссылка = СведенияОПользователях.Пользователь";

		Подзапрос = Новый Запрос(ПодзапросТекст);
		РедакторЗапроса.ПодзапросДобавить(Подзапрос);

		РедакторЗапроса.ПоследнийЗапросУсловиеДобавить("СведенияОПользователях.СрокДействия", ФильтрУсловиеСравения, ФильтрЗначение);

	ИначеЕсли ФильтрПоле = "АвторФайла" Тогда

		ПодзапросТекст = 
			"ВЫБРАТЬ
			|	БазовыйЗапрос.*
			|ИЗ
			|	БазовыйЗапрос КАК БазовыйЗапрос
			|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СведенияОФайлах КАК СведенияОФайлах
			|		ПО БазовыйЗапрос.Ссылка = СведенияОФайлах.Автор";

		Подзапрос = Новый Запрос(ПодзапросТекст);
		РедакторЗапроса.ПодзапросДобавить(Подзапрос);

		РедакторЗапроса.ПоследнийЗапросУсловиеДобавить("СведенияОФайлах.Файл", ФильтрУсловиеСравения, ФильтрЗначение);

	ИначеЕсли ФильтрПоле = "АвторПроцессов" Тогда

		ПодзапросТекст = 
			"ВЫБРАТЬ
			|	БазовыйЗапрос.*
			|ИЗ
			|	БазовыйЗапрос КАК БазовыйЗапрос
			|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ БизнесПроцесс.Задание КАК Задание
			|		ПО БазовыйЗапрос.Ссылка = Задание.Автор";

		Подзапрос = Новый Запрос(ПодзапросТекст);
		РедакторЗапроса.ПодзапросДобавить(Подзапрос);

		РедакторЗапроса.ПоследнийЗапросУсловиеДобавить("Задание.Ссылка", ФильтрУсловиеСравения, ФильтрЗначение);

	КонецЕсли;

КонецПроцедуры

 

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

	"ВЫБРАТЬ
	|	Пользователи.Ссылка КАК Ссылка
	|ПОМЕСТИТЬ БазовыйЗапрос_1
	|ИЗ
	|	Справочник.Пользователи КАК Пользователи
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	БазовыйЗапрос_1.Ссылка КАК Ссылка
	|ПОМЕСТИТЬ БазовыйЗапрос_2
	|ИЗ
	|	БазовыйЗапрос_1 КАК БазовыйЗапрос_1
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СведенияОПользователях КАК СведенияОПользователях
	|		ПО БазовыйЗапрос_1.Ссылка = СведенияОПользователях.Пользователь
	|ГДЕ
	|	СведенияОПользователях.СрокДействия > &СрокДействия_1
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|УНИЧТОЖИТЬ БазовыйЗапрос_1
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	БазовыйЗапрос_2.Ссылка КАК Ссылка
	|ПОМЕСТИТЬ БазовыйЗапрос_4
	|ИЗ
	|	БазовыйЗапрос_2 КАК БазовыйЗапрос_2
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СведенияОФайлах КАК СведенияОФайлах
	|		ПО БазовыйЗапрос_2.Ссылка = СведенияОФайлах.Автор
	|ГДЕ
	|	СведенияОФайлах.Файл = &Файл_3
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|УНИЧТОЖИТЬ БазовыйЗапрос_2
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	БазовыйЗапрос_4.Ссылка КАК Ссылка
	|ИЗ
	|	БазовыйЗапрос_4 КАК БазовыйЗапрос_4
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ БизнесПроцесс.Задание КАК Задание
	|		ПО БазовыйЗапрос_4.Ссылка = Задание.Автор
	|ГДЕ
	|	Задание.Ссылка = &Ссылка_5
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|УНИЧТОЖИТЬ БазовыйЗапрос_4"

Но при простоте обслуживания и реализации новых отборов, есть зависимость производительности от селективности примененных фильтров. В том случае если отборы высокоселективны, то во временные таблицы могут начать помещаться десятки тысяч строк и даже кочевать из одной временной таблицы в другую. Чтобы этого избежать, можно применить циклическую вложенную фильтрацию, реализованную в Редакторе запросов. Для этого перед компоновкой запроса необходимо выполнить команду:

	РедакторЗапроса.ЦиклическаяВложеннаяФильтрацияПрименить();

И текст запроса будет выглядеть следующим образом:

	"ВЫБРАТЬ
	|	БазовыйЗапрос_4.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.Пользователи КАК БазовыйЗапрос_4
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ БизнесПроцесс.Задание КАК Задание
	|		ПО БазовыйЗапрос_4.Ссылка = Задание.Автор
	|ГДЕ
	|	Задание.Ссылка = &Ссылка_5
	|	И БазовыйЗапрос_4.Ссылка В
	|			(ВЫБРАТЬ
	|				БазовыйЗапрос_2.Ссылка КАК Ссылка
	|			ИЗ
	|				Справочник.Пользователи КАК БазовыйЗапрос_2
	|					ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СведенияОФайлах КАК СведенияОФайлах
	|					ПО
	|						БазовыйЗапрос_2.Ссылка = СведенияОФайлах.Автор
	|			ГДЕ
	|				СведенияОФайлах.Файл = &Файл_3
	|				И БазовыйЗапрос_2.Ссылка В
	|					(ВЫБРАТЬ
	|						БазовыйЗапрос_1.Ссылка КАК Ссылка
	|					ИЗ
	|						Справочник.Пользователи КАК БазовыйЗапрос_1
	|							ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СведенияОПользователях КАК СведенияОПользователях
	|							ПО
	|								БазовыйЗапрос_1.Ссылка = СведенияОПользователях.Пользователь
	|					ГДЕ
	|						СведенияОПользователях.СрокДействия > &СрокДействия_1
	|						И БазовыйЗапрос_1.Ссылка В
	|							(ВЫБРАТЬ
	|								Пользователи.Ссылка КАК Ссылка
	|							ИЗ
	|								Справочник.Пользователи КАК Пользователи)))"

 

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

 

Как видите, все просто, Редактор запросов в помощь!

 

-----

Ссылки:
Обработка на Инфостарт

Вступайте в нашу телеграмм-группу Инфостарт

Оптимизация запросов Выборка данных Запросы

См. также

Инструментарий разработчика Роли и права Запросы СКД Программист Руководитель проекта 1С v8.3 Управляемые формы Запросы Система компоновки данных Платные (руб)

Инструменты для разработчиков 1С 8.3: Infostart Toolkit. Автоматизация и ускорение разработки на управляемых формах. Легкость работы с 1С.

15500 руб.

02.09.2020    215324    1187    413    

1052

Инструментарий разработчика Запросы Программист 1С v8.3 Сложные периодические расчеты Запросы 1С:Зарплата и кадры государственного учреждения 3 1С:Зарплата и Управление Персоналом 3.x Абонемент ($m)

QueryConsole1C — расширение, включающее консоль запросов с поддержкой исполняемых представлений — аналогов виртуальных таблиц, основанных на методах программного интерфейса ЗУП. Оно позволяет выполнять запросы с учётом встроенной бизнес-логики, отлаживать алгоритмы получения данных и автоматически генерировать код на встроенном языке 1С.

1 стартмани

16.05.2025    5393    89    zup_dev    20    

69

Инструментарий разработчика Запросы Программист 1С v8.3 Управляемые формы Запросы 1С:ERP Управление предприятием 2 Абонемент ($m)

В данной публикации рассказывается о решении, которое позволяет находить сразу все ошибки в тексте запроса за раз, а не только самую первую.

2 стартмани

05.03.2025    3960    17    XilDen    12    

26

Обновление 1С Запросы Программист 1С v8.3 1С:ERP Управление предприятием 2 Абонемент ($m)

Данный инструмент помогает анализировать доработанную конфигурацию после обновления на новый релиз и находить «битые» тексты запросов, в которых участвуют несуществующие в новом релизе метаданные.

3 стартмани

06.02.2025    3609    26    XilDen    26    

38

Запросы Программист 1С v8.3 Запросы 1C:Бухгалтерия Бесплатно (free)

В статье приведена удобная возможность отладки исполняемого запроса динамического списка.

03.12.2024    8536    artemusII    11    

24

Запросы Программист Бесплатно (free)

Увидел cheatsheet по SQL и захотелось нарисовать подобное, но про запросы.

18.10.2024    17079    sergey279    18    

70

Запросы Программист 1С v8.3 Запросы 1C:Бухгалтерия Бесплатно (free)

Столкнулся с интересной ситуацией, которую хотел бы разобрать, ввиду её неочевидности. Речь пойдёт про использование функции запроса АВТОНОМЕРЗАПИСИ() и проблемы, которые могут возникнуть.

11.10.2024    11728    XilDen    38    

104

СКД Механизмы типовых конфигураций Запросы Программист 1С v8.3 1С:Зарплата и кадры государственного учреждения 3 1С:Зарплата и Управление Персоналом 3.x Россия Бесплатно (free)

Работая с типовыми отчетами в конфигурациях «Зарплата и управление персоналом, редакция 3», «Зарплата и кадры государственного учреждения, редакция 3» и подобных, в схемах компоновки данных можно встретить конструкции запросов, которые обращаются к некоторым виртуальным таблицам.

20.08.2024    5709    PROSTO-1C    1    

28
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. aximo 2429 29.09.25 11:35 Сейчас в теме
слушайте, так что лучше - вложенный запрос или временная таблица все-таки?
3. ivshumelev 35 29.09.25 12:19 Сейчас в теме
(1) В нашем случае проблему с большими временными таблицами отлично решает вложенный запрос. На мой взгляд в принципе вложенный запрос быстрее, так как СУБД не нужно делать лишние операции создания и подсчета статистики временных таблиц. А сказать что кто то лучше или хуже наверное неправильно, всегда нужен баланс между производительностью и легкостью понимания/сопровождения.
4. paulwist 29.09.25 12:34 Сейчас в теме
В том случае если отборы высокоселективны, то во временные таблицы могут начать помещаться десятки тысяч строк и даже кочевать из одной временной таблицы в другую.


Поясните, почему при высокоселективных отборах во временные таблицы могут начать помещаться десятки тысяч строк, непонятно причём тут селективность?

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


А можете привести план с вложенным запросом?

PS что-то мне подсказывает, что оптимизатор упрощает и раскрывает вложенный запрос до исходных таблиц.
5. ivshumelev 35 29.09.25 12:58 Сейчас в теме
(4) Я имею ввиду высокоселктивный отбор - это который возвращает много строк. Если в первом пакете предположим 100 тыс. строк, а отбор отфильтровал только 1 запись, а остальные 99. тыс удовлетворяют этому отбору и попадают в выборку и так далее.

Извините, план не смотрел, и в ближайшее время в этом напавлении ничего сделать не смогу, но конечно очень интересно
2. webester 26 29.09.25 12:08 Сейчас в теме
(1)Серебряной пули нет. И ответа на ваш вопрос тоже.
6. Lem0n 435 29.09.25 13:51 Сейчас в теме
В каком воспаленном мозге могло родиться такое решение: на каждое соединение либо временная таблица, либо подзапрос? Вы говорите о производительности запросов, ну так покажите планы запросов.Стоимость плана, где Пользователи соединяться с тремя таблицами, будет ниже всего остального (все имеет стоимость и создание вт, и помещение туда данных тд). Изучите https://infostart.ru/1c/articles/1078667/
7. ivshumelev 35 29.09.25 14:05 Сейчас в теме
(6) Спасибо, ознакомлюсь. В статье это просто абстрактный пример, специально сделан для демонстрации, а также чтобы открывался конструктор запросов в одной БСП без привязки к каким либо другим конфигурациям. Я очень постараюсь приложить план реального запроса в течении 3ех месяцев, здесь приношу извинения.
Для отправки сообщения требуется регистрация/авторизация