При разработке отчетов в 1С:Предприятии начинающие разработчики часто сталкиваются с непониманием того, как работают механизмы вложенных запросов. Ситуация, когда запрос, который кажется логически верным с точки зрения классического SQL, возвращает пустой результат, встречается довольно часто. Давайте разберем, в чем заключается причина такого поведения и как правильно решать подобные аналитические задачи в среде 1С.
Проблема автора заключается в попытке использовать вложенный запрос как полноценный коррелированный подзапрос. В классических СУБД, таких как MS SQL Server, коррелированный подзапрос может обращаться к полям внешней таблицы на каждой итерации. В платформе 1С:Предприятие механизм корреляции ограничен конструкцией В (...).
Когда вы пишете запрос вида:
ВЫБРАТЬ
Т1.Ссылка
ИЗ
Документ.ОтчетПоПродажам.Продажи КАК Т1
ГДЕ
Т1.Ссылка В (
ВЫБРАТЬ
Т2.Ссылка
ИЗ
Документ.ОтчетПоПродажам.Продажи КАК Т2
ГДЕ
Т1.Номенклатура = Т2.Номенклатура
И Т1.ДатаПродажи < Т2.ДатаПродажи
)
Платформа не выполняет «цикл» в обычном понимании. Она ожидает, что вложенный запрос вернет набор данных, который будет использоваться как фильтр. Если условие Т1.ДатаПродажи < Т2.ДатаПродажи не выполняется ни для одной пары записей, вложенный запрос возвращает пустоту, а внешний запрос — закономерно пустой результат.
Основная причина — отсутствие поддержки полноценных коррелированных подзапросов в списке выбора или полноценных условиях JOIN вложенных запросов в 1С. В 1С вложенный запрос в условии ГДЕ ... В (...) работает как проверка на вхождение. Если для конкретной строки документа не нашлось ни одной записи, удовлетворяющей условию Т1.ДатаПродажи < Т2.ДатаПродажи, то для этой строки вернется ЛОЖЬ, и она будет исключена из выборки.
Для сравнения записей внутри одного набора данных в 1С существует два основных «золотых» стандарта разработки:
Это самый эффективный и надежный способ. Вместо коррелированного подзапроса мы используем обычное соединение, где одной и той же таблице присваиваются разные псевдонимы.
ВЫБРАТЬ
Т1.Ссылка КАК Ссылка,
Т1.Номенклатура КАК Номенклатура,
Т1.ДатаПродажи КАК ДатаПродажи
ИЗ
Документ.ОтчетПоПродажам.Продажи КАК Т1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ОтчетПоПродажам.Продажи КАК Т2
ПО Т1.Номенклатура = Т2.Номенклатура
И Т1.ДатаПродажи < Т2.ДатаПродажи
Такой подход позволяет платформе 1С оптимизировать план выполнения запроса, используя индексы по полям соединения (в данном случае по Номенклатура и ДатаПродажи).
Если задача требует сложной предварительной обработки (например, сначала нужно сгруппировать данные, а потом сравнить их с исходными), используйте пакетные запросы с ПОМЕСТИТЬ.
Шаги реализации:
Номенклатура).
ВЫБРАТЬ
Продажи.Номенклатура КАК Номенклатура,
МАКСИМУМ(Продажи.ДатаПродажи) КАК МаксДата
ПОМЕСТИТЬ ВремТаблица
ИЗ
Документ.ОтчетПоПродажам.Продажи КАК Продажи
СГРУППИРОВАТЬ ПО
Продажи.Номенклатура
;
ИНДЕКСИРОВАТЬ ПО
ВремТаблица.Номенклатура
;
ВЫБРАТЬ
*
ИЗ
Документ.ОтчетПоПродажам.Продажи КАК Продажи
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВремТаблица КАК Врем
ПО Продажи.Номенклатура = Врем.Номенклатура
Если вы не уверены, почему запрос не возвращает данные:
Т1) без контекста основного запроса.Т1.Номенклатура подставьте ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)).Использование Self-Join является предпочтительным способом решения задач, связанных с сравнением текущей записи с предыдущими или следующими в 1С:Предприятии. Это делает код читаемым, производительным и предсказуемым для платформы.