При работе с регистрами накопления в системе 1С:Предприятие начинающие разработчики часто сталкиваются с ситуацией, когда результат запроса содержит повторяющиеся (дублирующиеся) строки. На первый взгляд запрос кажется верным, но в отчет выводятся идентичные записи для одного и того же сотрудника или товара. В этой статье мы подробно разберем, почему это происходит, проанализируем ошибки в написании кода и научимся правильно использовать виртуальные таблицы.
Рассмотрим ситуацию, в которой разработчик пытается получить остатки по сотрудникам, но при этом хочет видеть данные за определенный период. В коде это часто выглядит следующим образом:
ВЫБРАТЬ
СотрудникиОстатки.Сотрудник КАК Сотрудник,
СотрудникиОстатки.Должность КАК Должность,
СотрудникиОстатки.СтавкаОстатокОстаток КАК СтавкаОстатокОстаток,
Сотрудники.Период КАК Период
ИЗ
РегистрНакопления.Сотрудники.Остатки КАК СотрудникиОстатки,
РегистрНакопления.Сотрудники КАК Сотрудники
ГДЕ
Сотрудники.Период <= &Период
Выясним причину дублей: В данном запросе выбраны сразу две таблицы: виртуальная таблица Остатки и основная физическая таблица регистра Сотрудники. Между ними не указано условие связи (отсутствует ключевое слово ПО или связь в блоке ГДЕ). В терминах баз данных и SQL это порождает декартово произведение (Cross Join). Каждая строка из таблицы остатков умножается на каждую строку из физической таблицы движений, которая подходит под условие по периоду. Если у сотрудника было 10 перемещений или начислений в истории, в результате вы получите 10 одинаковых строк с текущим остатком.
Проанализируем, чем отличаются таблицы, которые предоставляет платформа 1С. Для регистра накопления существуют:
Сотрудник или Должность — это норма, так как каждая запись соответствует конкретному событию во времени.Остатки: Она не существует в базе данных в явном виде. Система 1С формирует её "на лету", суммируя приходы и расходы из физических таблиц и итогов на указанный момент времени.Основное правило работы в 1С: если вам нужны актуальные данные на точку времени, вы должны обращаться только к виртуальной таблице, не смешивая её с физической без крайне специфической необходимости.
Чтобы получить корректные данные без дублей, необходимо передавать период и другие фильтры напрямую в параметры виртуальной таблицы. Рассмотрим исправленный вариант запроса:
ВЫБРАТЬ
СотрудникиОстатки.Сотрудник КАК Сотрудник,
СотрудникиОстатки.Должность КАК Должность,
СотрудникиОстатки.СтавкаОстатокОстаток КАК СтавкаОстатокОстаток
ИЗ
РегистрНакопления.Сотрудники.Остатки(&Период) КАК СотрудникиОстатки
Разберем подробнее, почему этот способ является единственно верным:
&Период в скобках после имени таблицы, СУБД (SQL Server или PostgreSQL) получает задачу рассчитать итоги именно на эту дату. Если же вы используете условие ГДЕ в конце запроса, система сначала может рассчитать остатки по всем записям, а потом начать их отсеивать, что крайне неэффективно.Сотрудник и Должность, то в таблице Остатки на каждую уникальную пару "Сотрудник + Должность" будет ровно одна строка.Посмотрим на процесс настройки через визуальный интерфейс, так как это помогает избежать синтаксических ошибок. Рассмотрим шаги настройки:
РегистрНакопления.ИмяРегистра.Остатки в левой колонке.&Период.Сотрудник = &ВыбСотрудник.Важный момент: Если вы хотите ограничить выборку конкретным сотрудником, делайте это именно в параметрах виртуальной таблицы, а не в общем списке условий запроса. Это значительно ускоряет работу программы в крупных базах.
Если вы применили конструкцию Остатки(&Период), но строки всё равно кажутся одинаковыми, проанализируем структуру самого регистра в Конфигураторе. Существует две скрытые причины "мнимых" дублей:
Допустим, в регистре есть три измерения: Сотрудник, Должность и Организация. В запросе вы выбираете только Сотрудник и Сумма. Если сотрудник работает в двух разных организациях, система выдаст две строки для этого сотрудника. Они будут выглядеть идентично, так как поле Организация не выведено в результат. Чтобы проверить это, попробуйте временно выбрать все измерения регистра в запросе.
Иногда дубль возникает из-за того, что в одной записи измерение заполнено, а в другой — нет (равно NULL или пустой ссылке). В визуальном отчете обе строки могут выглядеть похожими. Всегда проверяйте чистоту данных в регистре с помощью консоли запросов, выводя детальные записи через физическую таблицу с регистратором.
Часто путаница возникает из-за того, какая именно дата передается в параметр &Период. В 1С момент времени включает или не включает движения, совершенные в ту же секунду. Рассмотрим, как получить остатки максимально точно:
Если вам нужны остатки на конец дня, использование просто даты (например, 31.12.2023 23:59:59) может не включить документы, проведенные этой же секундой. Для решения этой задачи рекомендуется использовать объект Граница:
ГраницаПериода = Новый Граница(КонецДня(НужнаяДата), ВидГраницы.Включая);
Запрос.УстановитьПараметр("Период", ГраницаПериода);
Это гарантирует, что все движения, сделанные в последнюю секунду дня, будут учтены при расчете остатков в виртуальной таблице.
Подводя итог, разберем краткий чек-лист для проверки запроса на "дубли":
.Остатки), а не физическую.ПО.Соблюдение этих правил позволит вам писать быстрые и корректные запросы, которые всегда возвращают ожидаемые данные без лишнего "шума" и повторений.