При разработке отчетов и механизмов проведения документов на платформе 1С программисты часто сталкиваются с выбором: написать один сложный запрос с несколькими вложенными подзапросами или использовать пакет запросов с временными таблицами. На первый взгляд, вложенный запрос может показаться компактным и простым решением, однако на практике он несет в себе множество технических рисков и скрытых проблем с производительностью. Проанализируем подробно все недостатки вложенных запросов, выясним причины нестабильной работы оптимизатора СУБД и разберем правильные подходы к построению запросов в 1С.
Главная техническая проблема вложенного запроса заключается в том, что оптимизатор планировщика СУБД (будь то MS SQL Server или PostgreSQL) не располагает гистограммой распределения и статистикой по промежуточному результату. Рассмотрим, как это происходит на этапе выполнения:
Nested Loops (вложенные петли), что приводит к длительным зависаниям сервера СУБД.Результат вложенного запроса формируется на лету и сохраняется в оперативной памяти или во временной структуре СУБД без каких-либо индексов. Проанализируем ситуацию, когда выполняется соединение реальной таблицы базы данных с вложенным запросом по определённому полю (например, Номенклатура или Договор):
Так как у вложенного запроса нет индекса по полю соединения, СУБД не может применить точечный поиск. В результате сервер вынужден выполнять полный просмотр строк (Table Scan или Index Scan). Это приводит к колоссальному росту нагрузки на процессор и дисковую подсистему.
Система компоновки данных в 1С динамически модифицирует и оптимизирует исходный текст запроса в зависимости от выбранных пользователем полей, группировок и отборов. При работе со сложными вложенными конструкциями механика СКД может дать сбой:
ОБЪЕДИНИТЬ ВСЕ) или соединения с другими временными структурами, динамическая оптимизация СКД может некорректно вырезать «ненужные» поля и привести к получению пустого результата или вызову ошибки выполнения.Если вложенный запрос используется внутри условия соединения (секция ПО) или в секции фильтрации ГДЕ, существует опасность создания коррелированного подзапроса. В этом случае СУБД будет выполнять вложенный запрос повторно для каждой строки внешней таблицы.
Посмотрим на пример: если во внешнем источнике содержится 50 000 документов, а во вложенном запросе определяется срез последних цен, подзапрос может выполниться 50 000 раз. Алгоритмическая сложность возрастает до O(N²), что приводит к полной остановке работы системы.
С точки зрения чистой разработки и командной работы, вложенные запросы обладают следующими недостатками:
Стандартом разработки на платформе 1С является отказ от соединений со вложенными запросами в пользу **пакета запросов с временными таблицами**. Разберем по шагам, почему этот подход решает все описанные проблемы:
ПОМЕСТИТЬ мы можем сразу проиндексировать ключевые поля с помощью инструкции ИНДЕКСИРОВАТЬ ПО.Рассмотрев теорию, проанализируем пример нерекомендуемого запроса со соединением с подзапросом и перепишем его на правильную структуру с использованием временных таблиц.
Нерекомендуемый вариант (соединение со вложенным запросом):
ВЫБРАТЬ
Продажи.Номенклатура КАК Номенклатура,
Продажи.Количество КАК Количество,
Цены.Цена КАК Цена
ИЗ
РегистрНакопления.Продажи КАК Продажи
ЛЕВОЕ СОЕДИНЕНИЕ (
ВЫБРАТЬ
ЦеныНоменклатуры.Номенклатура КАК Номенклатура,
ЦеныНоменклатуры.Цена КАК Цена
ИЗ
РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ) КАК ЦеныНоменклатуры
) КАК Цены
ПО Продажи.Номенклатура = Цены.Номенклатура
Рекомендуемый вариант (пакет запросов с временной таблицей и индексацией):
ВЫБРАТЬ
ЦеныНоменклатуры.Номенклатура КАК Номенклатура,
ЦеныНоменклатуры.Цена КАК Цена
ПОМЕСТИТЬ ВТ_Цены
ИЗ
РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ) КАК ЦеныНоменклатуры
ИНДЕКСИРОВАТЬ ПО
Номенклатура
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Продажи.Номенклатура КАК Номенклатура,
Продажи.Количество КАК Количество,
ВТ_Цены.Цена КАК Цена
ИЗ
РегистрНакопления.Продажи КАК Продажи
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Цены КАК ВТ_Цены
ПО Продажи.Номенклатура = ВТ_Цены.Номенклатура
;
УНИЧТОЖИТЬ ВТ_Цены
Подводя итог, отметим: единственное исключение, когда использование вложенного запроса допустимо — это простые отборы в секции ГДЕ вида Ссылка В (ВЫБРАТЬ ...) с гарантированно небольшим списком значений. Во всех остальных случаях при работе с большими объемами данных следует строго придерживаться правил стандарта разработки 1С и применять временные таблицы с явным индексированием полей соединения.