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

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

При построении сложных запросов и отчетов по учету НДФЛ и заработной плате нередко возникает задача объединить две временные таблицы по связке полей, например: ФизЛицо, Организация и КПП. Однако на практике КПП обособленного подразделения или организации может быть не указан (содержать пустую строку или NULL). В результате стандартное условие связи перестает срабатывать, либо из выборки полностью пропадают данные первой таблицы.

Разберем детально, почему так происходит, какие типичные ошибки допускают разработчики и какими способами можно реализовать корректное соединение таблиц.

Анализ проблемы: почему пропадают строки при левом соединении

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

  1. Условия отбора в секции ГДЕ: если в запросе или в настройках отборов СКД наложено условие на поле правой таблицы (например, ГДЕ ВТ2.Поле2 = &ЗначениеОтбора), то при отсутствии связи поле возвращает NULL. Сравнение NULL = &ЗначениеОтбора возвращает ЛОЖЬ, и строка левой таблицы полностью отсекается, превращая левое соединение во внутреннее.
  2. Специфика сравнения пустых строк и значений NULL: в базе данных значение NULL не равно пустой строке "". Более того, при сравнении строк разной длины или разного типа (фиксированная и переменная длина) пустые значения могут не совпадать.

Ошибочные подходы к решению

Посмотрим на примеры выражений, которые часто пытаются написать разработчики в секции ПО, и выясним, почему они не работают:

Попытка использовать условную конструкцию, возвращающую булево значение или строку:


// Неверно: синтаксическая ошибка платформы
ВЫБОР КОГДА ВТОсновнойОтчет.КПП ЕСТЬ NULL ТОГДА "" ИНАЧЕ ВТПодразделение.КПП = ВТОсновнойОтчет.КПП КОНЕЦ

В конструкции ПО должны использоваться корректные логические предикаты. Платформа 1С не поддерживает возврат строки внутри логического условия соединения.

Другая распространенная ошибка — использование условий с логическим ИЛИ:


// Не рекомендуется: приводит к дублированию строк и отключению индексов
ПО ВТ1.ФизЛицо = ВТ2.ФизЛицо
И ВТ1.Организация = ВТ2.Организация
И (ВТ1.КПП = ВТ2.КПП ИЛИ ВТ2.КПП = "")

Такое условие приводит к катастрофической потере производительности, так как СУБД не может применить алгоритмы Hash Join и переходит к полному сканированию. Кроме того, если у сотрудника во второй таблице есть строки как с заполненным, так и с пустым КПП, возникнет декартово произведение и задвоение числовых показателей.

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

Если требуется связать записи, где пустой КПП считается равнозначным отсутствующему значению, самым простым решением является приведение значений к единому формату с помощью функции ЕСТЬNULL.

Рассмотрим пример условия связи в запросе:


ВЫБРАТЬ
    ВТ1.ФизЛицо КАК ФизЛицо,
    ВТ1.Организация КАК Организация,
    ВТ1.КПП КАК КПП,
    ВТ1.СуммаДохода КАК СуммаДохода,
    ЕСТЬNULL(ВТ2.СуммаНалога, 0) КАК СуммаНалога
ИЗ
    ВТОсновнойОтчет КАК ВТ1
    ЛЕВОЕ СОЕДИНЕНИЕ ВТПодразделения КАК ВТ2
        ПО ВТ1.ФизЛицо = ВТ2.ФизЛицо
        И ВТ1.Организация = ВТ2.Организация
        И ЕСТЬNULL(ВТ1.КПП, "") = ЕСТЬNULL(ВТ2.КПП, "")

Чтобы гарантировать совпадение строковых полей на уровне СУБД (MS SQL Server или PostgreSQL), рекомендуется явно ограничить длину строки с помощью ВЫРАЗИТЬ еще на этапе помещения данных во временные таблицы:

ВЫРАЗИТЬ(ЕСТЬNULL(Таблица.КПП, "") КАК СТРОКА(9)) КАК КПП

Способ 2. Двухэтапное приоритетное соединение

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

Разберем по шагам реализацию этой схемы:

  1. Присоединяем вторую таблицу в первый раз по точному совпадению всех трех полей (строгое соединение).
  2. Присоединяем ту же вторую таблицу второй раз, но только для записей, где КПП пустой.
  3. В итоговых полях выбираем значения по приоритету через ВЫБОР КОГДА или ЕСТЬNULL.

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


ВЫБРАТЬ
    Основная.ФизЛицо КАК ФизЛицо,
    Основная.Организация КАК Организация,
    Основная.КПП КАК КПП,
    Основная.БазаНДФЛ КАК БазаНДФЛ,
    // Если найдено точное совпадение по КПП — берем его, иначе берем данные без КПП:
    ЕСТЬNULL(ТочныеДанные.Налог, ЕСТЬNULL(ОбщиеДанные.Налог, 0)) КАК НалогНДФЛ
ИЗ
    ВТОсновнойОтчет КАК Основная
    
    // 1. Точное соединение по всем реквизитам
    ЛЕВОЕ СОЕДИНЕНИЕ ВТНалогНДФЛ КАК ТочныеДанные
        ПО Основная.ФизЛицо = ТочныеДанные.ФизЛицо
        И Основная.Организация = ТочныеДанные.Организация
        И Основная.КПП = ТочныеДанные.КПП
        
    // 2. Дополнительное соединение для случаев, когда КПП не указан
    ЛЕВОЕ СОЕДИНЕНИЕ ВТНалогНДФЛ КАК ОбщиеДанные
        ПО Основная.ФизЛицо = ОбщиеДанные.ФизЛицо
        И Основная.Организация = ОбщиеДанные.Организация
        И (ОбщиеДанные.КПП = "" ИЛИ ОбщиеДанные.КПП ЕСТЬ NULL)

Такой подход полностью исключает искажение сумм, позволяет оптимизатору СУБД использовать индексы временных таблиц и гарантирует, что данные левой таблицы останутся в результирующей выборке независимо от заполненности реквизита КПП.

← На главную