В практике разработки на платформе 1С:Предприятие часто возникает задача выборки данных из документа, который содержит несколько табличных частей. Классический пример — документ выплаты зарплаты (например, «Ведомость в банк» или «Ведомость в кассу» в конфигурациях ЗУП), где начисления находятся в одной табличной части (Зарплата), а удержанный налог — в другой (НДФЛ). Подобные задачи регулярно встречаются при разработке отчетов, таких как начисления, НДФЛ и взносы за период для ЗУП 3.1. Нам необходимо получить плоскую таблицу, где для каждого сотрудника в одной строке будут выведены и сумма к выплате, и сумма налога.
Рассмотрим подробно, как правильно решить эту задачу, избежав распространенных ошибок, таких как дублирование строк (декартово произведение) и потеря данных при отсутствии записей в одной из таблиц.
Имеем документ с двумя табличными частями:
ФизическоеЛицо, КВыплате, НомерЛицевогоСчета.ФизическоеЛицо, Сумма (налог), СуммаСПревышения.Нам необходимо получить выборку, которая часто требуется для анализа НДФЛ по документам начислений за месяц:
Самый простой способ, который приходит на ум, — выбрать основную таблицу (например, Зарплата) и присоединить к ней таблицу НДФЛ. Давайте проанализируем запрос, предложенный в ходе обсуждения.
ВЫБРАТЬ
ВедомостьЗарплата.КВыплате КАК КВыплате,
ВедомостьЗарплата.ФизическоеЛицо КАК ФизическоеЛицо,
ВедомостьЗарплата.НомерЛицевогоСчета КАК НомерЛицевогоСчета,
ВложенныйЗапрос.Сумма КАК Сумма,
ВложенныйЗапрос.СуммаСПревышения КАК СуммаСПревышения
ИЗ
Документ.ВедомостьНаВыплатуЗарплатыВБанк.Зарплата КАК ВедомостьЗарплата
ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
ВедомостьНДФЛ.ФизическоеЛицо КАК ФизическоеЛицо,
ВедомостьНДФЛ.Сумма КАК Сумма,
ВедомостьНДФЛ.СуммаСПревышения КАК СуммаСПревышения,
ВедомостьНДФЛ.Ссылка КАК Ссылка
ИЗ
Документ.ВедомостьНаВыплатуЗарплатыВБанк.НДФЛ КАК ВедомостьНДФЛ) КАК ВложенныйЗапрос
ПО ВедомостьЗарплата.Ссылка = ВложенныйЗапрос.Ссылка
И ВедомостьЗарплата.ФизическоеЛицо = ВложенныйЗапрос.ФизическоеЛицо
ГДЕ
ВедомостьЗарплата.Ссылка = &Ссылка
Преимущества: Простота реализации. Если мы уверены, что строки по НДФЛ всегда сопровождаются строками выплаты, этот метод сработает.
Недостатки:
НДФЛ (например, возврат налога или удержание без выплаты), но нет строки в таблице Зарплата, этот сотрудник не попадет в выборку.Зарплата по разным основаниям и одна в НДФЛ), данные могут "задвоиться" при соединении, исказив итоговые суммы.Чтобы не потерять данные из второй таблицы (в нашем случае НДФЛ), если их нет в первой, следует использовать ПОЛНОЕ СОЕДИНЕНИЕ. Это позволит выбрать записи, присутствующие в любой из таблиц.
При использовании полного соединения важно помнить про поля, по которым идет объединение (в данном случае ФизическоеЛицо). Поскольку запись может отсутствовать в одной из таблиц, просто выбрать поле Таблица1.ФизическоеЛицо нельзя — оно может оказаться NULL. Необходимо использовать функцию ЕСТЬNULL.
Рассмотрим скорректированный пример с использованием агрегатных функций для корректного суммирования:
ВЫБРАТЬ
ЕСТЬNULL(ВедомостьЗарплата.ФизическоеЛицо, ВложенныйНДФЛ.ФизическоеЛицо) КАК ФизическоеЛицо,
ВедомостьЗарплата.НомерЛицевогоСчета КАК НомерЛицевогоСчета,
СУММА(ЕСТЬNULL(ВедомостьЗарплата.КВыплате, 0)) КАК КВыплате,
СУММА(ЕСТЬNULL(ВложенныйНДФЛ.Сумма, 0)) КАК СуммаНДФЛ,
СУММА(ЕСТЬNULL(ВложенныйНДФЛ.СуммаСПревышения, 0)) КАК СуммаСПревышения
ИЗ
Документ.ВедомостьНаВыплатуЗарплатыВБанк.Зарплата КАК ВедомостьЗарплата
ПОЛНОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
ТабНДФЛ.ФизическоеЛицо КАК ФизическоеЛицо,
ТабНДФЛ.Ссылка КАК Ссылка,
СУММА(ТабНДФЛ.Сумма) КАК Сумма,
СУММА(ТабНДФЛ.СуммаСПревышения) КАК СуммаСПревышения
ИЗ
Документ.ВедомостьНаВыплатуЗарплатыВБанк.НДФЛ КАК ТабНДФЛ
ГДЕ
ТабНДФЛ.Ссылка = &Ссылка
СГРУППИРОВАТЬ ПО
ТабНДФЛ.ФизическоеЛицо,
ТабНДФЛ.Ссылка) КАК ВложенныйНДФЛ
ПО ВедомостьЗарплата.Ссылка = ВложенныйНДФЛ.Ссылка
И ВедомостьЗарплата.ФизическоеЛицо = ВложенныйНДФЛ.ФизическоеЛицо
ГДЕ
(ВедомостьЗарплата.Ссылка = &Ссылка ИЛИ ВедомостьЗарплата.Ссылка ЕСТЬ NULL)
СГРУППИРОВАТЬ ПО
ЕСТЬNULL(ВедомостьЗарплата.ФизическоеЛицо, ВложенныйНДФЛ.ФизическоеЛицо),
ВедомостьЗарплата.НомерЛицевогоСчета
Обратите внимание на то, что вложенный запрос по НДФЛ предварительно группируется. Это критически важный момент, который мы разберем далее.
Самая опасная ошибка при соединении двух табличных частей — возникновение дублей.
Представим ситуацию:
Зарплата 2 строки (например, оплата по окладу и премия).НДФЛ 2 строки (например, налог с оклада и налог с материальной выгоды).Если мы соединим эти таблицы напрямую по ФизическоеЛицо, СУБД сопоставит каждую строку из первой таблицы с каждой строкой из второй. В результате мы получим 2 * 2 = 4 строки. При суммировании итогов суммы увеличатся в 2 раза. Для визуального контроля и отладки сложных выборок удобно использовать такой инструмент, как Консоль запросов УФ, которая позволяет быстро анализировать промежуточные результаты.
Решение: Перед соединением необходимо свернуть (сгруппировать) каждую табличную часть до уникальных ключей (в нашем случае — до ФизЛица). То есть сначала мы делаем два вложенных запроса (или две временные таблицы), где группируем данные по ФизЛицу, и только потом соединяем полученные результаты.
Этот метод часто является наиболее надежным и простым для понимания, особенно если вам не нужны уникальные данные из одной таблицы для строк другой (как, например, номер лицевого счета, который есть только в первой таблице).
Алгоритм действий следующий:
Зарплата, проставляя 0 или NULL в полях для НДФЛ.НДФЛ, проставляя 0 или NULL в полях для Зарплаты.ОБЪЕДИНИТЬ ВСЕ.ФизическоеЛицо, суммируя показатели.
ВЫБРАТЬ
ОбщаяТаблица.ФизическоеЛицо КАК ФизическоеЛицо,
МАКСИМУМ(ОбщаяТаблица.НомерЛицевогоСчета) КАК НомерЛицевогоСчета,
СУММА(ОбщаяТаблица.КВыплате) КАК КВыплате,
СУММА(ОбщаяТаблица.НДФЛ) КАК НДФЛ
ИЗ
(ВЫБРАТЬ
ТЧ_Зарплата.ФизическоеЛицо КАК ФизическоеЛицо,
ТЧ_Зарплата.НомерЛицевогоСчета КАК НомерЛицевогоСчета,
ТЧ_Зарплата.КВыплате КАК КВыплате,
0 КАК НДФЛ
ИЗ
Документ.ВедомостьНаВыплатуЗарплатыВБанк.Зарплата КАК ТЧ_Зарплата
ГДЕ
ТЧ_Зарплата.Ссылка = &Ссылка
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ТЧ_НДФЛ.ФизическоеЛицо,
NULL, // Номера счета тут нет
0, // Суммы к выплате тут нет
ТЧ_НДФЛ.Сумма
ИЗ
Документ.ВедомостьНаВыплатуЗарплатыВБанк.НДФЛ КАК ТЧ_НДФЛ
ГДЕ
ТЧ_НДФЛ.Ссылка = &Ссылка) КАК ОбщаяТаблица
СГРУППИРОВАТЬ ПО
ОбщаяТаблица.ФизическоеЛицо
В этом примере мы используем функцию МАКСИМУМ для поля НомерЛицевогоСчета, так как в строках из таблицы НДФЛ оно равно NULL. Если у человека один лицевой счет, функция вернет его корректно.
Подводя итог, выделим основные правила работы с запросами к нескольким табличным частям:
ПОЛНОЕ СОЕДИНЕНИЕ, если важно не потерять данные ни из одной таблицы.ПОЛНОЕ СОЕДИНЕНИЕ не забывайте про конструкцию ЕСТЬNULL для полей связи.ОБЪЕДИНИТЬ ВСЕ и последующей группировкой является самым безопасным с точки зрения избежания дублей.В современных конфигурациях 1С (например, ЗУП 3.1) в табличных частях часто присутствует поле ИдентификаторСтроки. Если оно есть в обеих табличных частях и заполнено корректно (то есть налог привязан к конкретной строке начисления), соединение следует выполнять именно по этому полю. Это даст наиболее точный результат без необходимости группировок.