Как убрать дубликаты в запросе при соединении регистров штатного расписания в 1С?

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

При разработке отчетов или обработок в конфигурациях на базе 1С:Зарплата и управление персоналом 3.1 программисты часто сталкиваются с проблемой "размножения" строк при попытке получить данные из регистров сведений, связанных со штатным расписанием. Ситуация, описанная в теме, типична: при добавлении в запрос таблицы РегистрСведений.ИсторияНачисленийПоШтатномуРасписанию данные начинают задваиваться или даже застраиваться. Давайте разберем по шагам, почему это происходит и как правильно составить запрос, чтобы получить корректный результат.

Выясняем причину возникновения дублей

Проанализируем ситуацию. В 1С дубликаты при левом соединении возникают тогда, когда одной записи из левой (основной) таблицы соответствует несколько записей из правой (присоединяемой) таблицы. В случае с регистрами ИсторияПоказателейПоШтатномуРасписанию и ИсторияНачисленийПоШтатномуРасписанию проблема кроется в структуре самих данных. Эти регистры хранят информацию в разрезе позиций штатного расписания и документов-регистраторов, но этого недостаточно для однозначной связи строк.

Рассмотрим подробнее: на одну позицию штатного расписания одним и тем же документом может быть назначено несколько начислений (например, "Оклад" и "Надбавка за вредность"). Каждому начислению соответствуют свои показатели. Если мы связываем таблицы только по полям ПозицияШтатногоРасписания и Регистратор, система выполняет декартово произведение записей в рамках этой пары. То есть каждый показатель "Оклада" соединится не только со строкой "Оклада", но и со строкой "Надбавки", что и приведет к дублированию.

Решение 1: Использование технического ключа связи

Для корректного соединения этих двух регистров необходимо использовать дополнительное поле связи — ИдентификаторСтрокиВидаРасчета. Это техническое поле специально предназначено для того, чтобы связать конкретную запись начисления с конкретными записями его показателей.

Исправим условие связи в запросе. Вместо упрощенного варианта:


ПО (ИсторияПоказателей.ПозицияШтатногоРасписания = ИсторияНачислений.ПозицияШтатногоРасписания)
И (ИсторияПоказателей.Регистратор = ИсторияНачислений.Регистратор)

Нам необходимо написать более точное условие:


ПО ИсторияПоказателей.ПозицияШтатногоРасписания = ИсторияНачислений.ПозицияШтатногоРасписания
    И ИсторияПоказателей.Регистратор = ИсторияНачислений.Регистратор
    И ИсторияПоказателей.ИдентификаторСтрокиВидаРасчета = ИсторияНачислений.ИдентификаторСтрокиВидаРасчета

Это позволит системе четко сопоставить показатель конкретному начислению, исключая "перекрестное" соединение лишних строк.

Решение 2: Фильтрация по активности записей

Еще одна неочевидная причина появления лишних строк — работа с записями, которые были отменены или сторнированы. В регистрах сведений 1С есть служебное поле Активность. Если в запросе не указано обратное, система выбирает все записи, включая неактивные (технические или помеченные на удаление).

Проанализируем ситуацию: если документ редактировался или исправлялся, в базе могут остаться записи с Активность = ЛОЖЬ. Чтобы они не попадали в выборку, всегда добавляйте условие в секцию ГДЕ:


ГДЕ
    ИсторияПоказателей.Активность = ИСТИНА
    И ИсторияНачислений.Активность = ИСТИНА

Решение 3: Использование временных таблиц и индексации

Как справедливо отмечено в обсуждении, сложные запросы с соединениями лучше разбивать на этапы. Это не только упрощает отладку в консоли запросов, но и повышает производительность. Разберем этот метод по шагам:

  1. Выбираем данные первого регистра во временную таблицу. На этом этапе мы можем сразу отсечь ненужные показатели или позиции.
  2. Индексируем временную таблицу. Это критически важно для быстрого выполнения ЛЕВОЕ СОЕДИНЕНИЕ. Индексировать нужно те поля, по которым будет происходить связь (ПозицияШтатногоРасписания, Регистратор, ИдентификаторСтрокиВидаРасчета).
  3. Присоединяем вторую таблицу. Теперь, когда данные подготовлены и проиндексированы, вероятность получить непредсказуемый результат гораздо ниже.

Пример структуры такого запроса:


// Шаг 1: Подготовка показателей
ВЫБРАТЬ
    ИсторияПоказателей.ПозицияШтатногоРасписания,
    ИсторияПоказателей.Регистратор,
    ИсторияПоказателей.ИдентификаторСтрокиВидаРасчета,
    ИсторияПоказателей.Значение
ПОМЕСТИТЬ ВТ_Показатели
ИЗ
    РегистрСведений.ИсторияПоказателейПоШтатномуРасписанию КАК ИсторияПоказателей
ГДЕ
    ИсторияПоказателей.Активность = ИСТИНА;

////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Соединение с начислениями
ВЫБРАТЬ
    ВТ_Показатели.ПозицияШтатногоРасписания,
    ИсторияНачислений.Начисление,
    ВТ_Показатели.Значение
ИЗ
    ВТ_Показатели КАК ВТ_Показатели
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ИсторияНачисленийПоШтатномуРасписанию КАК ИсторияНачислений
        ПО ВТ_Показатели.ПозицияШтатногоРасписания = ИсторияНачислений.ПозицияШтатногоРасписания
            И ВТ_Показатели.Регистратор = ИсторияНачислений.Регистратор
            И ВТ_Показатели.ИдентификаторСтрокиВидаРасчета = ИсторияНачислений.ИдентификаторСтрокиВидаРасчета
ГДЕ
    ИсторияНачислений.Активность = ИСТИНА

Решение 4: Группировка и использование функции ЕСТЬNULL

Если после добавления всех условий связи дубликаты сохраняются (например, из-за специфики ввода данных пользователями), мы можем применить агрегацию. Использование СГРУППИРОВАТЬ ПО позволяет свернуть идентичные строки, выбрав максимальное или суммарное значение для числовых полей.

Также важно помнить, что при ЛЕВОЕ СОЕДИНЕНИЕ для записей, которым не нашлось соответствия в правой таблице, будет возвращено значение NULL. Это может мешать фильтрации. Рассмотрим пример использования функции ЕСТЬNULL для обработки таких случаев:


ВЫБРАТЬ
    Данные.ПозицияШтатногоРасписания,
    ЕСТЬNULL(Данные.Начисление, "Начисление не задано") КАК Начисление,
    СУММА(Данные.Значение) КАК Значение
ИЗ
    (...) КАК Данные
СГРУППИРОВАТЬ ПО
    Данные.ПозицияШтатногоРасписания,
    ЕСТЬNULL(Данные.Начисление, "Начисление не задано")

Рекомендации по отладке

Для того чтобы окончательно победить дубликаты, рекомендуем выполнить следующие действия:

Таким образом, правильное решение проблемы дублей заключается в точном определении ключей связи между таблицами и фильтрации технических записей. Использование ИдентификаторСтрокиВидаРасчета в связке с контролем Активность решит задачу в большинстве случаев при работе со штатным расписанием в 1С.

← На главную