При построении сложных запросов и отчетов по учету НДФЛ и заработной плате нередко возникает задача объединить две временные таблицы по связке полей, например: ФизЛицо, Организация и КПП. Однако на практике КПП обособленного подразделения или организации может быть не указан (содержать пустую строку или NULL). В результате стандартное условие связи перестает срабатывать, либо из выборки полностью пропадают данные первой таблицы.
Разберем детально, почему так происходит, какие типичные ошибки допускают разработчики и какими способами можно реализовать корректное соединение таблиц.
Классическая логика работы ЛЕВОЕ СОЕДИНЕНИЕ заключается в том, что все строки левой (первой) таблицы обязательно должны попасть в итоговую выборку, даже если в правой таблице соответствие не найдено. Если строки всё же исчезают, причина чаще всего кроется в одной из двух ошибок:
ГДЕ ВТ2.Поле2 = &ЗначениеОтбора), то при отсутствии связи поле возвращает NULL. Сравнение NULL = &ЗначениеОтбора возвращает ЛОЖЬ, и строка левой таблицы полностью отсекается, превращая левое соединение во внутреннее.NULL не равно пустой строке "". Более того, при сравнении строк разной длины или разного типа (фиксированная и переменная длина) пустые значения могут не совпадать.Посмотрим на примеры выражений, которые часто пытаются написать разработчики в секции ПО, и выясним, почему они не работают:
Попытка использовать условную конструкцию, возвращающую булево значение или строку:
// Неверно: синтаксическая ошибка платформы
ВЫБОР КОГДА ВТОсновнойОтчет.КПП ЕСТЬ NULL ТОГДА "" ИНАЧЕ ВТПодразделение.КПП = ВТОсновнойОтчет.КПП КОНЕЦ
В конструкции ПО должны использоваться корректные логические предикаты. Платформа 1С не поддерживает возврат строки внутри логического условия соединения.
Другая распространенная ошибка — использование условий с логическим ИЛИ:
// Не рекомендуется: приводит к дублированию строк и отключению индексов
ПО ВТ1.ФизЛицо = ВТ2.ФизЛицо
И ВТ1.Организация = ВТ2.Организация
И (ВТ1.КПП = ВТ2.КПП ИЛИ ВТ2.КПП = "")
Такое условие приводит к катастрофической потере производительности, так как СУБД не может применить алгоритмы Hash Join и переходит к полному сканированию. Кроме того, если у сотрудника во второй таблице есть строки как с заполненным, так и с пустым КПП, возникнет декартово произведение и задвоение числовых показателей.
Если требуется связать записи, где пустой КПП считается равнозначным отсутствующему значению, самым простым решением является приведение значений к единому формату с помощью функции ЕСТЬNULL.
Рассмотрим пример условия связи в запросе:
ВЫБРАТЬ
ВТ1.ФизЛицо КАК ФизЛицо,
ВТ1.Организация КАК Организация,
ВТ1.КПП КАК КПП,
ВТ1.СуммаДохода КАК СуммаДохода,
ЕСТЬNULL(ВТ2.СуммаНалога, 0) КАК СуммаНалога
ИЗ
ВТОсновнойОтчет КАК ВТ1
ЛЕВОЕ СОЕДИНЕНИЕ ВТПодразделения КАК ВТ2
ПО ВТ1.ФизЛицо = ВТ2.ФизЛицо
И ВТ1.Организация = ВТ2.Организация
И ЕСТЬNULL(ВТ1.КПП, "") = ЕСТЬNULL(ВТ2.КПП, "")
Чтобы гарантировать совпадение строковых полей на уровне СУБД (MS SQL Server или PostgreSQL), рекомендуется явно ограничить длину строки с помощью ВЫРАЗИТЬ еще на этапе помещения данных во временные таблицы:
ВЫРАЗИТЬ(ЕСТЬNULL(Таблица.КПП, "") КАК СТРОКА(9)) КАК КПП
В налоговом и зарплатном учете пустой КПП часто означает регистрацию в налоговом органе по головной организации, в то время как заполненный КПП относится к конкретному обособленному подразделению. В такой ситуации идеальным решением является двойное левое соединение с разделением по приоритету.
Разберем по шагам реализацию этой схемы:
ВЫБОР КОГДА или ЕСТЬNULL.Посмотрим на пример практической реализации:
ВЫБРАТЬ
Основная.ФизЛицо КАК ФизЛицо,
Основная.Организация КАК Организация,
Основная.КПП КАК КПП,
Основная.БазаНДФЛ КАК БазаНДФЛ,
// Если найдено точное совпадение по КПП — берем его, иначе берем данные без КПП:
ЕСТЬNULL(ТочныеДанные.Налог, ЕСТЬNULL(ОбщиеДанные.Налог, 0)) КАК НалогНДФЛ
ИЗ
ВТОсновнойОтчет КАК Основная
// 1. Точное соединение по всем реквизитам
ЛЕВОЕ СОЕДИНЕНИЕ ВТНалогНДФЛ КАК ТочныеДанные
ПО Основная.ФизЛицо = ТочныеДанные.ФизЛицо
И Основная.Организация = ТочныеДанные.Организация
И Основная.КПП = ТочныеДанные.КПП
// 2. Дополнительное соединение для случаев, когда КПП не указан
ЛЕВОЕ СОЕДИНЕНИЕ ВТНалогНДФЛ КАК ОбщиеДанные
ПО Основная.ФизЛицо = ОбщиеДанные.ФизЛицо
И Основная.Организация = ОбщиеДанные.Организация
И (ОбщиеДанные.КПП = "" ИЛИ ОбщиеДанные.КПП ЕСТЬ NULL)
Такой подход полностью исключает искажение сумм, позволяет оптимизатору СУБД использовать индексы временных таблиц и гарантирует, что данные левой таблицы останутся в результирующей выборке независимо от заполненности реквизита КПП.