Как правильно заполнить таблицу значений из разных источников и избежать дублирования строк

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

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

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

Рассмотрим ситуацию, описанную автором: данные выбираются из регистра оборотов и регистра сведений. В процессе выполнения цикла Пока Выборка.Следующий() в таблицу значений добавляются строки, но поля заполняются некорректно — значения из одного источника начинают "наслаиваться" на строки другого.

Основная причина кроется в архитектуре запроса. Автор признается, что использует полное соединение по условию ИСТИНА. В теории баз данных это называется Декартовым произведением (Cross Join). Проанализируем, что это значит: если в первой таблице 10 строк, а во второй 5, то результатом соединения по ИСТИНА станет таблица из 50 строк (10 * 5). Система пытается сопоставить каждую строку первой таблицы с каждой строкой второй. Именно поэтому в результате появляются повторяющиеся данные, а поле Регистратор заполняется там, где оно должно быть пустым.

Решение №1: Использование конструкции ОБЪЕДИНИТЬ ВСЕ

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


ТекстЗапроса = "
|ВЫБРАТЬ
|   РегистрОборотов.Регистратор КАК Регистратор,
|   РегистрОборотов.СуммаОборот КАК Значение
|ИЗ
|   РегистрНакопления.Продажи.Обороты(&Начало, &Конец, Регистратор, ) КАК РегистрОборотов
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
|   НЕОПРЕДЕЛЕНО, // Поле Регистратор оставляем пустым для второго источника
|   РегистрСведений.Значение КАК Значение
|ИЗ
|   РегистрСведений.ДополнительныеСведения КАК РегистрСведений";

Преимущества этого метода:

  1. Отсутствие дублей: Данные просто идут друг за другом. Сначала строки из первого регистра, затем из второго.
  2. Типизация: Мы явно указываем, что во втором запросе поле Регистратор должно быть заполнено значением NULL или Неопределено.
  3. Производительность: Операция ОБЪЕДИНИТЬ ВСЕ работает значительно быстрее, чем тяжелые соединения, особенно декартовы.

После выполнения такого запроса нам не нужно использовать цикл Пока ... Цикл. Достаточно выгрузить результат одной командой:


РезультатЗапроса = Запрос.Выполнить();
СписокКл = РезультатЗапроса.Выгрузить();

Решение №2: Исправление логики заполнения в цикле

Если по какой-то причине объединение в запросе невозможно, и вам приходится обходить выборку вручную, важно понимать разницу между методами Добавить() и обращением к существующей строке. Посмотрим на типичную ошибку в коде:


// ОШИБОЧНЫЙ КОД
Пока Выборка.Следующий() Цикл 
    НоваяСтрока = СписокКл.Добавить(); 
    НоваяСтрока.Регистратор = Выборка.Регистратор; 
КонецЦикла;

Метод Добавить() всегда создает новую запись в конце таблицы. Если у вас уже были данные в таблице из первого источника, этот цикл просто добавит новые строки снизу, оставив колонки первого источника пустыми. Если же вы хотите заполнить данные в уже существующих строках, нужно использовать индекс или найти нужную строку.

Проанализируем ситуацию, когда нужно заполнить данные только в первой строке или в конкретном диапазоне. Рассмотрим пример использования индекса:


Если Выборка.Следующий() Тогда
    // Заполняем только ПЕРВУЮ строку, если она уже создана
    Если СписокКл.Количество() > 0 Тогда
        СписокКл[0].Регистратор = Выборка.Регистратор;
    КонецЕсли;
КонецЕсли;

Нюансы работы с полем Регистратор

Важно помнить технические особенности объектов 1С. В регистрах оборотов поле Регистратор является стандартным и доступно почти всегда. Однако в регистрах сведений ситуация иная:

Если вы программно создаете колонки в ТаблицеЗначений, убедитесь, что колонке Регистратор назначен Составной тип. В противном случае, при попытке записать туда ссылку на документ другого типа (или Неопределено), значение может просто не сохраниться, что автор темы ошибочно принимает за "пустое поле".

Использование метода ЗагрузитьКолонку

Рассмотрим еще один эффективный способ, если у вас есть массив данных, который нужно "влить" в существующую таблицу. Допустим, у вас есть ТаблицаЗначений, в которой уже создано 10 строк. Вы получили массив регистраторов из другого источника. Вместо циклов можно использовать:


// МассивРегистраторов должен иметь ту же длину, что и ТЗ
СписокКл.ЗагрузитьКолонку(МассивРегистраторов, "Регистратор");

Это заполнит существующие строки сверху вниз значениями из массива. Это гораздо быстрее и чище с точки зрения кода.

Подведение итогов

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

  1. Избегайте Соединений по ИСТИНЕ. Всегда задавайте четкое условие связи (например, по номенклатуре, контрагенту или периоду).
  2. Используйте ОБЪЕДИНИТЬ ВСЕ для получения данных из разных регистров в один плоский список.
  3. Выгружайте запрос целиком. Использование РезультатЗапроса.Выгрузить() избавляет от ошибок ручного создания строк в цикле.
  4. Следите за типами данных. Колонка Регистратор должна поддерживать все типы документов, которые вы планируете в нее поместить.

Разберем финальный пример правильного подхода к задаче:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Продажи.Регистратор КАК Регистратор,
    |   Продажи.СуммаОборот КАК Сумма
    |ИЗ
    |   РегистрНакопления.Продажи.Обороты КАК Продажи
    |
    |ОБЪЕДИНИТЬ ВСЕ
    |
    |ВЫБРАТЬ
    |   ЗНАЧЕНИЕ(Документ.РеализацияТоваровУслуг.ПустаяСсылка),
    |   Сведения.Значение
    |ИЗ
    |   РегистрСведений.Настройки КАК Сведения";

ТаблицаРезультатов = Запрос.Выполнить().Выгрузить();
// Теперь ТаблицаРезультатов содержит корректные данные без лишних строк

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

← На главную