Как работает коррелированный подзапрос в 1С и конструкция «ИСТИНА В (ВЫБРАТЬ ПЕРВЫЕ 1...)»?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С:Предприятие часто встречаются сложные запросы, логика которых не всегда очевидна на первый взгляд. Один из таких примеров — использование подзапроса внутри условия ГДЕ, который обращается к полям основной таблицы. Разберем детально, как работают подобные конструкции, почему они допустимы и как их интерпретирует система. Для тестирования и интерактивной отладки подобных конструкций можно использовать консоль запросов для управляемых форм.

Суть вопроса: почему видна основная таблица?

Рассмотрим представленный в теме запрос. В нем выбираются данные из таблицы РегистрСведений.СостоянияПредметовВзаимодействий, а в секции ГДЕ прописано условие, содержащее вложенный запрос. Главный вопрос, который часто возникает у разработчиков: почему внутри вложенного запроса доступно поле СостоянияПредметовВзаимодействий.Предмет, хотя эта таблица не описана в секции ИЗ подзапроса?

Проанализируем ситуацию: это классический пример коррелированного подзапроса. В SQL и языке запросов 1С вложенный запрос может ссылаться на поля таблиц, находящихся на более высоких уровнях иерархии запроса. Область видимости полей «пробрасывается» внутрь подзапроса. С точки зрения логики выполнения, это можно представить как проверку условия для каждой строки внешней таблицы.

Разбор паттерна «ИСТИНА В (ВЫБРАТЬ ПЕРВЫЕ 1...)»

Конструкция ИСТИНА В (ВЫБРАТЬ ПЕРВЫЕ 1 ИСТИНА...) — это стандартный для 1С способ реализации оператора EXISTS, который существует в классическом SQL, но отсутствует в языке запросов платформы. Выясним причину использования именно такого подхода:

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

Пошаговый алгоритм работы запроса

Рассмотрим подробнее, что происходит при выполнении этого кода:

1. Система берет очередную строку из таблицы РегистрСведений.СостоянияПредметовВзаимодействий.

2. Для этой строки выполняется подзапрос, в который подставляется значение Предмет из текущей строки внешней таблицы.

3. Внутри подзапроса происходит соединение ПредметыПапкиВзаимодействий и ЖурналДокументов.Взаимодействия по конкретному предмету и ответственному.

4. Если подзапрос возвращает хотя бы одну строку со значением ИСТИНА, условие в ГДЕ для внешней строки считается выполненным.

Посмотрим на пример структуры запроса, который мы анализируем:


ВЫБРАТЬ
    СостоянияПредметовВзаимодействий.Предмет КАК Предмет
ИЗ
    РегистрСведений.СостоянияПредметовВзаимодействий КАК СостоянияПредметовВзаимодействий
ГДЕ
    ИСТИНА В
            (ВЫБРАТЬ ПЕРВЫЕ 1
                ИСТИНА
            ИЗ
                РегистрСведений.ПредметыПапкиВзаимодействий КАК ПредметыВзаимодействий
                    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ЖурналДокументов.Взаимодействия КАК ЖурналДокументовВзаимодействия
                    ПО ПредметыВзаимодействий.Взаимодействие = ЖурналДокументовВзаимодействия.Ссылка
                        И ЖурналДокументовВзаимодействия.Ответственный = &Пар
            ГДЕ
                ПредметыВзаимодействий.Предмет = СостоянияПредметовВзаимодействий.Предмет
                И ЕСТЬNULL(ПредметыВзаимодействий.Рассмотрено, ЛОЖЬ) = &Пар2)

Место условий: ПО или ГДЕ?

В ходе обсуждения на форуме возник спор о том, где лучше указывать условия соединения. Выясним истину. Существует общее правило: условия связи таблиц должны находиться в секции ПО, а условия фильтрации итоговой выборки — в секции ГДЕ.

Однако, проанализируем ситуацию для ВНУТРЕННЕГО СОЕДИНЕНИЯ: оптимизаторы современных СУБД (MS SQL, PostgreSQL) работают достаточно эффективно, чтобы переносить фильтры из ГДЕ в план соединения автоматически. То есть физический план выполнения запроса будет идентичным. Но с точки зрения читаемости кода и стандартов разработки, условия связи таблиц всегда предпочтительнее держать в ПО.

Важное исключение — ЛЕВОЕ СОЕДИНЕНИЕ. Если вы перенесете условие из секции ПО в секцию ГДЕ для левого соединения, оно фактически превратится во внутреннее, так как условие ГДЕ отсечет все строки, где значения из правой таблицы равны NULL.

Производительность коррелированных подзапросов

Многие разработчики считают коррелированные подзапросы «медленными», сравнивая их с циклами. Но это не совсем так. Рассмотрим факторы, влияющие на скорость:

В качестве альтернативы иногда рассматривают использование временных таблиц. Разберем по шагам, как это могло бы выглядеть:

  1. Сначала мы выбираем все подходящие пары «Предмет-Взаимодействие» во временную таблицу.
  2. Индексируем временную таблицу по полю Предмет.
  3. Соединяем основную таблицу с временной.

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

Выводы

Подводя итог, можно сказать, что запрос в примере написан вполне грамотно для задач 1С. Он эффективно проверяет существование связи, не создавая дублей в результате. Основные моменты, которые мы выяснили:

— Область видимости таблиц в языке запросов 1С позволяет обращаться к полям верхнего уровня из подзапроса.

— Конструкция ИСТИНА В (ВЫБРАТЬ ПЕРВЫЕ 1...) — это эффективная замена оператора EXISTS.

— Для корректной работы таких запросов критически важно наличие индексов по полям связи.

— При работе с ВНУТРЕННИМ СОЕДИНЕНИЕМ разница между расположением условий в ПО и ГДЕ чаще всего эстетическая, но для ЛЕВОГО СОЕДИНЕНИЯ она критична.

← На главную