В чем разница между условиями в секциях ПО и ГДЕ в запросах 1С

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

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

Логический порядок выполнения запроса

Прежде всего, нам необходимо понять, в какой последовательности система обрабатывает запрос. Несмотря на то что мы пишем ВЫБРАТЬ в самом начале, физически запрос выполняется иначе. Рассмотрим упрощенную схему:

  1. Секция ИЗ (FROM): Выбираются основные таблицы.
  2. Секция ПО (ON): Определяются правила, по которым строки одной таблицы будут сопоставляться со строками другой.
  3. СОЕДИНЕНИЕ (JOIN): Происходит фактическое объединение данных.
  4. Секция ГДЕ (WHERE): К уже полученному общему набору данных применяется финальный фильтр.

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

Разберем секцию ГДЕ: Фильтрация результата

Секция ГДЕ — это жесткое сито. Если после объединения таблиц строка не удовлетворяет условию, она полностью удаляется из итогового набора данных. Рассмотрим ситуацию, когда нам нужно найти всех контрагентов, по которым не было оплат.

Проанализируем пример кода:


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

В данном случае мы сначала берем всех контрагентов, присоединяем к ним документы оплат (там, где их нет, получаем NULL), а затем с помощью ГДЕ отсекаем всех, у кого оплата нашлась. Это классический и правильный способ решения задачи "найти отсутствие записей".

Разберем секцию ПО: Правила соединения

Секция ПО определяет, какие именно записи из правой таблицы (например, Оплаты) мы хотим видеть рядом с записями из левой таблицы (Контрагенты). При использовании ЛЕВОГО СОЕДИНЕНИЯ важно помнить правило: левая таблица всегда попадает в результат целиком, независимо от выполнения условий в секции ПО.

Если условие в ПО не выполняется, то для данной строки левой таблицы поля правой таблицы просто заполняются значением NULL, но сама строка не исчезает.

Рассмотрим ошибочный вариант, который часто встречается в практике:


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

Выясним причину, почему это не работает. Условие Оплаты.Ссылка ЕСТЬ NULL проверяется в момент соединения. Но в исходной таблице Документ.Оплата поле Ссылка никогда не равно NULL (там всегда есть конкретная ссылка на документ). Значение NULL рождается только как результат неудачного соединения. Пытаться фильтровать по NULL внутри ПО — это логический тупик. В итоге условие ПО никогда не выполнится, и мы получим список всех контрагентов, к которым ничего не присоединилось. Это фактически равносильно отсутствию соединения вообще.

Ловушка превращения Левого соединения во Внутреннее

Одна из самых коварных ошибок — наложение условий на правую таблицу в секции ГДЕ. Допустим, мы хотим вывести всех контрагентов и их оплаты по конкретной организации.

Посмотрим на пример, который может привести к потере данных:


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

Что здесь произойдет? Если у контрагента не было оплат, то поле Оплаты.Организация будет содержать NULL. Условие NULL = &Организация всегда ложно. В результате контрагенты без оплат будут удалены из отчета. Ваше ЛЕВОЕ СОЕДИНЕНИЕ превратилось во ВНУТРЕННЕЕ. Чтобы сохранить всех контрагентов, условие по организации нужно перенести в секцию ПО:


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

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

Работа с виртуальными таблицами

Особое внимание следует уделить виртуальным таблицам (остатки, обороты). В 1С крайне не рекомендуется использовать ГДЕ для фильтрации данных виртуальных таблиц. Правильный путь — использование параметров виртуальной таблицы.

Проанализируем разницу:

Плохо:


ВЫБРАТЬ
    Продажи.Номенклатура,
    Продажи.КоличествоОборот
ИЗ
    РегистрНакопления.Продажи.Обороты КАК Продажи
ГДЕ
    Продажи.Номенклатура В (&СписокТоваров)

Хорошо:


ВЫБРАТЬ
    Продажи.Номенклатура,
    Продажи.КоличествоОборот
ИЗ
    РегистрНакопления.Продажи.Обороты(, , , Номенклатура В (&СписокТоваров)) КАК Продажи

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

Краткая шпаргалка для разработчика

Подводя итог, сформулируем основные правила выбора места для условия в запросах 1С:

Применяя эти правила, вы сможете писать не только корректные, но и высокопроизводительные запросы, которые всегда возвращают именно те данные, которые вы ожидаете увидеть.

← На главную