При переходе на платформу 1С разработчики с опытом работы в классических СУБД (MS SQL Server, Oracle, PostgreSQL) часто сталкиваются с ограничением: в языке запросов 1С нельзя написать вложенный подзапрос непосредственно в секции ВЫБРАТЬ (скалярный подзапрос вида SELECT A.Поле, (SELECT TOP 1 B.Значение FROM B WHERE B.ID = A.ID ORDER BY B.Дата) FROM A). Разберем причины этого ограничения, выясним, как обойти его в 1С с помощью условий соединения, и рассмотрим альтернативные варианты решения задачи.
Проанализируем особенности синтаксиса платформы:
ВЫБРАТЬ формировались исключительно из источников данных, выражений, встроенных функций или агрегатов. Вставить запрос в круглых скобках в список полей невозможно.CROSS APPLY / OUTER APPLY: Платформа не предоставляет прямой синтаксической конструкции для построчной передачи параметров во внешнюю табличную функцию или сопоставленный подзапрос.ГДЕ, однако синтаксис секции ПО (в условиях соединений) позволяет связывать поля основной таблицы с вложенным запросом.Рассмотрим прием, позволяющий сымитировать логику TOP 1 с сортировкой непосредственно в одном запросе без предварительного создания временных таблиц. Мы помещаем подзапрос с конструкцией ПЕРВЫЕ 1 и оператором УПОРЯДОЧИТЬ ПО прямо в условие связи ПО оператора ЛЕВОЕ СОЕДИНЕНИЕ.
Посмотрим на пример реализации:
ВЫБРАТЬ
Основная.Ссылка КАК Ссылка,
Основная.Период КАК Период,
ТабЗначений.Значение КАК Значение
ИЗ
Документ.РеализацияТоваровУслуг КАК Основная
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК ТабЗначений
ПО ТабЗначений.Период В
(ВЫБРАТЬ ПЕРВЫЕ 1
ТабОтбор.Период
ИЗ
РегистрСведений.КурсыВалют КАК ТабОтбор
ГДЕ
ТабОтбор.Валюта = Основная.ДоговорКонтрагента.ВалютаВзаиморасчетов
И ТабОтбор.Период <= Основная.Период
УПОРЯДОЧИТЬ ПО
ТабОтбор.Период УБЫВ)
Разберем по шагам, как работает данный запрос:
Основная платформа генерирует условие отбора во вложенной выборке ТабОтбор.Валюта) и граничному условию (Период).УПОРЯДОЧИТЬ ПО совместно с ПЕРВЫЕ 1 оставляет ровно одну строку с нужным экстремумом.ТабЗначений только по полученному ключевому значению, гарантируя отсутствие дублирования строк.Важное предостережение по производительности: На уровне СУБД такое условие транслируется во вложенный цикл (Nested Loops). Если по соединяемым полям и полям сортировки нет точного покрывающего индекса, СУБД будет выполнять сканирование таблицы для каждой строки левой таблицы. На больших выборках это может вызвать сильные задержки.
Если требуется обработать большой объем данных на высоконагруженной системе, правильнее использовать стандартный шаблон с агрегацией и индексированными временными таблицами. Это позволяет СУБД применить быстрые алгоритмы соединения (Hash Match или Merge Join).
Посмотрим на пример с пакетом запросов:
ВЫБРАТЬ
Основная.Ссылка КАК Документ,
Основная.Валюта КАК Валюта,
МАКСИМУМ(Курсы.Период) КАК ПериодКурса
ПОМЕСТИТЬ ВТ_Срезы
ИЗ
Документ.ЗаказКлиента КАК Основная
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК Курсы
ПО Основная.Валюта = Курсы.Валюта
И Основная.Дата >= Курсы.Период
СГРУППИРОВАТЬ ПО
Основная.Ссылка,
Основная.Валюта
;
ИНДЕКСИРОВАТЬ ПО
Валюта,
ПериодКурса
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_Срезы.Документ КАК Документ,
КурсыВалют.Курс КАК Курс
ИЗ
ВТ_Срезы КАК ВТ_Срезы
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют
ПО ВТ_Срезы.Валюта = КурсыВалют.Валюта
И ВТ_Срезы.ПериодКурса = КурсыВалют.Период
Преимущества этого подхода:
ИНДЕКСИРОВАТЬ ПО.Если задача решается в рамках Системы компоновки данных (СКД), можно обойтись без сложных условий соединения в самом тексте запроса:
НаборДанных1 (основная таблица) и НаборДанных2 (подчиненная таблица).ВЫБРАТЬ ПЕРВЫЕ 1 Поле Из Таблица ГДЕ Ключ = &ПараметрКлюча УПОРЯДОЧИТЬ ПО Дата УБЫВ.&ПараметрКлюча.