Как правильно наложить условие на левую таблицу в запросе 1С

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

При работе с запросами в системе 1С:Предприятие одной из самых распространенных задач является соединение двух или более таблиц. Однако начинающие разработчики часто сталкиваются с проблемой: при использовании ЛЕВОЕ СОЕДИНЕНИЕ попытка отфильтровать данные левой таблицы непосредственно в условии связи (секция ПО) не приносит желаемого результата. В этой статье мы подробно разберем, почему это происходит, как работает логика SQL-запросов и какие методы фильтрации являются наиболее эффективными и правильными с точки зрения стандартов разработки.

Почему условие в секции ПО не фильтрует левую таблицу

Разберем ситуацию, которая часто вводит в заблуждение. Представим, что у нас есть Таблица1 (левая) и Таблица2 (правая). Мы хотим получить все записи из первой таблицы, но только те, где Поле2 равно определенному параметру, и присоединить к ним данные из второй таблицы. Проанализируем ошибочный подход:


ВЫБРАТЬ
    Т1.Ссылка,
    Т2.Наименование
ИЗ
    Справочник.Номенклатура КАК Т1
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Цены КАК Т2
        ПО Т1.Ссылка = Т2.Номенклатура
            И Т1.Поле2 = &Параметр1

Выясним причину, по которой это не работает. Согласно фундаментальной логике LEFT JOIN, результат запроса всегда должен содержать все строки из левой таблицы, удовлетворяющие условиям в секции ГДЕ. Условие в секции ПО (ON) определяет лишь то, какие записи из правой таблицы будут сопоставлены с левой. Если условие Т1.Поле2 = &Параметр1 не выполняется для какой-то строки из Т1, то 1С (и СУБД) не удаляет эту строку из результата. Она просто не находит для нее соответствия в Т2 и выводит в полях правой таблицы значение NULL.

Таким образом, условие на левую таблицу в секции ПО превращается в условие на правую: "Присоединить правую таблицу только к тем строкам левой, где выполняется условие, а для остальных оставить NULL". Сама левая таблица при этом остается нетронутой.

Способ 1: Использование секции ГДЕ (WHERE)

Самый простой способ отфильтровать итоговый результат — перенести условие в секцию ГДЕ. Давайте посмотрим, как изменится логика:


ВЫБРАТЬ
    Т1.Ссылка,
    Т2.Наименование
ИЗ
    Справочник.Номенклатура КАК Т1
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Цены КАК Т2
        ПО Т1.Ссылка = Т2.Номенклатура
ГДЕ
    Т1.Поле2 = &Параметр1

В этом случае СУБД сначала выполнит соединение таблиц, а затем применит фильтр ко всей выборке. Это рабочий вариант, однако здесь есть критически важный нюанс при работе с правой таблицей. Рассмотрим ситуацию: если вы наложите условие на поле правой таблицы в секции ГДЕ (например, ГДЕ Т2.ТипЦены = &ТипЦены), ваше ЛЕВОЕ СОЕДИНЕНИЕ автоматически превратится во ВНУТРЕННЕЕ. Это произойдет потому, что строки, для которых в правой таблице не нашлось данных (где значения полей равны NULL), будут отсечены фильтром, так как сравнение NULL = &Параметр всегда дает ложь.

Способ 2: Предварительная фильтрация во временных таблицах (Рекомендуемый)

Методисты фирмы "1С" рекомендуют использовать временные таблицы для подготовки данных перед соединением. Это не только делает код более читаемым, но и зачастую ускоряет выполнение запроса, особенно на больших объемах данных. Проанализируем этот метод по шагам:

  1. Создаем временную таблицу с уже отфильтрованными данными из левой таблицы.
  2. Индексируем временную таблицу по полям связи.
  3. Выполняем соединение уже подготовленной временной таблицы с другими данными.

Пример реализации:


// Помещаем отфильтрованную левую таблицу во временную
ВЫБРАТЬ
    Номенклатура.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_ОтфильтрованнаяНоменклатура
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.Поле2 = &Параметр1
ИНДЕКСИРОВАТЬ ПО
    Ссылка
;

// Соединяем подготовленные данные
ВЫБРАТЬ
    Т1.Ссылка,
    Т2.Наименование
ИЗ
    ВТ_ОтфильтрованнаяНоменклатура КАК Т1
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Цены КАК Т2
        ПО Т1.Ссылка = Т2.Номенклатура

Преимущества этого подхода:

Способ 3: Подзапросы в тексте запроса

Если задача разовая и данных немного, можно использовать подзапросы непосредственно в секции ИЗ. Это избавляет от необходимости плодить временные таблицы, но может быть менее производительным.


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

Важно помнить, что современные оптимизаторы СУБД часто "разворачивают" такие подзапросы обратно в плоскую структуру, поэтому с точки зрения итогового плана выполнения этот вариант может не отличаться от использования секции ГДЕ.

Оптимизация при получении множества свойств (Pattern EAV)

Часто левое соединение используется для получения дополнительных реквизитов. Если у вас 10-20 дополнительных реквизитов, делать 20 левых соединений к одной и той же таблице — крайне неэффективно. Рассмотрим более производительный метод ОБЪЕДИНИТЬ ВСЕ с последующей группировкой, который обсуждался экспертами:


ВЫБРАТЬ
    Т.Ссылка КАК Ссылка,
    МАКСИМУМ(Т.Свойство1) КАК Свойство1,
    МАКСИМУМ(Т.Свойство2) КАК Свойство2
ИЗ
    (ВЫБРАТЬ
        Номенклатура.Ссылка КАК Ссылка,
        NULL КАК Свойство1,
        NULL КАК Свойство2
    ИЗ
        Справочник.Номенклатура КАК Номенклатура
    ГДЕ
        Номенклатура.Ссылка В (&МассивСсылок)

    ОБЪЕДИНИТЬ ВСЕ

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

В этом примере мы делаем всего один проход по таблице дополнительных реквизитов, вместо множества соединений. Функция МАКСИМУМ позволяет "схлопнуть" строки, отсеивая значения NULL, полученные из ОБЪЕДИНИТЬ ВСЕ.

Подведем итоги

Проанализировав различные подходы к фильтрации данных при левом соединении, можно сделать следующие выводы:

  1. Никогда не ставьте условие фильтрации левой таблицы в секцию ПО — оно там просто не будет работать как фильтр строк.
  2. Для простых запросов используйте секцию ГДЕ, но следите за тем, чтобы случайно не превратить левое соединение во внутреннее при фильтрации по правой таблице.
  3. Для сложных, высоконагруженных систем всегда используйте временные таблицы с предварительной фильтрацией и индексацией. Это золотой стандарт разработки на платформе 1С.
  4. Помните об особенностях СУБД: MS SQL Server может простить многие ошибки оптимизации, в то время как PostgreSQL гораздо чувствительнее к структуре запроса и требует более аккуратного подхода к подзапросам и соединениям.

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

← На главную