Как в запросе 1С выбрать значение из правой таблицы при левом соединении, если строка найдена, и из левой — если нет?

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

При составлении запросов в 1С часто возникает задача: объединить две таблицы по набору ключевых полей (например, Период, Партнер, Номенклатура) и взять цену из второй (правой) таблицы, если соответствие найдено, а если строка отсутствует — сохранить базовую цену из первой (левой) таблицы.

Нередко разработчики пытаются решить эту задачу путем дублирования условий связи внутри оператора ВЫБОР или использованием сложных отрицаний, что усложняет код и приводит к ошибкам. Разберем, как правильно реализовать такую логику в языке запросов 1С и какие подводные камни могут встретиться.

Анализ типичной ошибки: дублирование условий соединения

Рассмотрим пример запроса, в котором автор пытается повторить условие соединения в секции ВЫБОР:


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

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

Поскольку в SQL и языке запросов 1С любое сравнение со значением NULL (например, Поле = NULL) всегда возвращает Ложь, ветка ТОГДА не срабатывает. Однако писать такую громоздкую конструкцию избыточно. Кроме того, присутствие в исходном запросе блока СГРУППИРОВАТЬ ПО без агрегатных функций (СУММА, МАКСИМУМ) и с включением полей обеих цен приводит к непредсказуемым результатам при дублях строк.

Способ 1. Использование функции ЕСТЬNULL (базовый вариант)

Наиболее лаконичный и быстрый способ подставить альтернативное значение — использовать встроенную функцию ЕСТЬNULL. Если строка из правой таблицы соединилась, поле ВТ_ЧислителиНоменклатуры.Цена вернет конкретное число. Если соответствие не найдено, значением поля будет NULL, и функция автоматически вернет второй аргумент — цену из левой таблицы.

Посмотрим на пример реализации:


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

Этот вариант компактен, отлично читается оптимизатором СУБД и является стандартом в разработке на платформе 1С.

Способ 2. Проверка ключевого поля на ЕСТЬ НЕ NULL

Бывают ситуации, когда в самой правой таблице поле Цена может содержать значение NULL или 0. Если применить ЕСТЬNULL(ВТ_ЧислителиНоменклатуры.Цена, ...), то даже при найденной строке система может ошибочно подтянуть левую цену, посчитав, что соединение не удалось.

Чтобы гарантированно проверить сам факт успешного соединения строк, проверяют ключевое непустое поле правой таблицы (например, ссылку на номенклатуру):


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

Почему соединение может не работать: частые причины

Если код написан правильно, но подстановка цен все равно не происходит или данные задваиваются, разберем возможные скрытые причины в исходных таблицах:

  1. Несовпадение формата даты в поле ПериодМесяц: Если в одной таблице дата содержит произвольное время (например, 15.03.2023 14:30:00), а во второй приведена к началу месяца (01.03.2023 00:00:00), равенство никогда не выполнится. Обе таблицы при формировании должны содержать даты, приведенные функцией НАЧАЛОПЕРИОДА(Период, МЕСЯЦ).
  2. Составные типы и пустые значения: Если реквизиты Партнер или Номенклатура имеют составной тип, в одной таблице может оказаться пустая ссылка конкретного типа (ЗНАЧЕНИЕ(...)), а в другой — НЕОПРЕДЕЛЕНО или NULL. В этом случае они не будут равны. Для надежности типы приводят конструкцией ВЫРАЗИТЬ(... КАК ...).
  3. Дублирование строк в правой таблице: Если связка (ПериодМесяц, Партнер, Номенклатура) в правой таблице неуникальна, левое соединение размножит строки из левой таблицы. Правую таблицу перед соединением необходимо подготовить — сгруппировать по ключевым полям с вычислением агрегата цены.
  4. Лишняя секция группировки: Если в запросе нет агрегатных функций (СУММА, КОЛИЧЕСТВО), блок СГРУППИРОВАТЬ ПО использовать не нужно. Для исключения дублирующихся строк следует использовать инструкцию ВЫБРАТЬ РАЗЛИЧНЫЕ.
← На главную