При разработке в среде 1С:Предприятие одной из самых распространенных задач является консолидация данных из нескольких источников (например, из различных регистров) в одну общую Таблицу Значений (ТЗ). Однако новички часто сталкиваются с проблемой появления "лишних" строк или дублирования данных. Разберем по шагам, почему так происходит и как этого избежать.
Рассмотрим ситуацию, описанную автором: данные выбираются из регистра оборотов и регистра сведений. В процессе выполнения цикла Пока Выборка.Следующий() в таблицу значений добавляются строки, но поля заполняются некорректно — значения из одного источника начинают "наслаиваться" на строки другого.
Основная причина кроется в архитектуре запроса. Автор признается, что использует полное соединение по условию ИСТИНА. В теории баз данных это называется Декартовым произведением (Cross Join). Проанализируем, что это значит: если в первой таблице 10 строк, а во второй 5, то результатом соединения по ИСТИНА станет таблица из 50 строк (10 * 5). Система пытается сопоставить каждую строку первой таблицы с каждой строкой второй. Именно поэтому в результате появляются повторяющиеся данные, а поле Регистратор заполняется там, где оно должно быть пустым.
Вместо того чтобы пытаться "склеить" таблицы горизонтально через соединения, когда между ними нет четкой логической связи по ключевым полям, лучше использовать вертикальное объединение. Рассмотрим, как правильно структурировать запрос:
ТекстЗапроса = "
|ВЫБРАТЬ
| РегистрОборотов.Регистратор КАК Регистратор,
| РегистрОборотов.СуммаОборот КАК Значение
|ИЗ
| РегистрНакопления.Продажи.Обороты(&Начало, &Конец, Регистратор, ) КАК РегистрОборотов
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| НЕОПРЕДЕЛЕНО, // Поле Регистратор оставляем пустым для второго источника
| РегистрСведений.Значение КАК Значение
|ИЗ
| РегистрСведений.ДополнительныеСведения КАК РегистрСведений";
Преимущества этого метода:
Регистратор должно быть заполнено значением NULL или Неопределено.ОБЪЕДИНИТЬ ВСЕ работает значительно быстрее, чем тяжелые соединения, особенно декартовы.После выполнения такого запроса нам не нужно использовать цикл Пока ... Цикл. Достаточно выгрузить результат одной командой:
РезультатЗапроса = Запрос.Выполнить();
СписокКл = РезультатЗапроса.Выгрузить();
Если по какой-то причине объединение в запросе невозможно, и вам приходится обходить выборку вручную, важно понимать разницу между методами Добавить() и обращением к существующей строке. Посмотрим на типичную ошибку в коде:
// ОШИБОЧНЫЙ КОД
Пока Выборка.Следующий() Цикл
НоваяСтрока = СписокКл.Добавить();
НоваяСтрока.Регистратор = Выборка.Регистратор;
КонецЦикла;
Метод Добавить() всегда создает новую запись в конце таблицы. Если у вас уже были данные в таблице из первого источника, этот цикл просто добавит новые строки снизу, оставив колонки первого источника пустыми. Если же вы хотите заполнить данные в уже существующих строках, нужно использовать индекс или найти нужную строку.
Проанализируем ситуацию, когда нужно заполнить данные только в первой строке или в конкретном диапазоне. Рассмотрим пример использования индекса:
Если Выборка.Следующий() Тогда
// Заполняем только ПЕРВУЮ строку, если она уже создана
Если СписокКл.Количество() > 0 Тогда
СписокКл[0].Регистратор = Выборка.Регистратор;
КонецЕсли;
КонецЕсли;
Важно помнить технические особенности объектов 1С. В регистрах оборотов поле Регистратор является стандартным и доступно почти всегда. Однако в регистрах сведений ситуация иная:
Регистратор в нем нет. Попытка обратиться к нему вызовет ошибку.Если вы программно создаете колонки в ТаблицеЗначений, убедитесь, что колонке Регистратор назначен Составной тип. В противном случае, при попытке записать туда ссылку на документ другого типа (или Неопределено), значение может просто не сохраниться, что автор темы ошибочно принимает за "пустое поле".
Рассмотрим еще один эффективный способ, если у вас есть массив данных, который нужно "влить" в существующую таблицу. Допустим, у вас есть ТаблицаЗначений, в которой уже создано 10 строк. Вы получили массив регистраторов из другого источника. Вместо циклов можно использовать:
// МассивРегистраторов должен иметь ту же длину, что и ТЗ
СписокКл.ЗагрузитьКолонку(МассивРегистраторов, "Регистратор");
Это заполнит существующие строки сверху вниз значениями из массива. Это гораздо быстрее и чище с точки зрения кода.
Для того чтобы вторая (или любая другая) строка не заполнялась лишними данными, следуйте этим правилам:
РезультатЗапроса.Выгрузить() избавляет от ошибок ручного создания строк в цикле.Регистратор должна поддерживать все типы документов, которые вы планируете в нее поместить.Разберем финальный пример правильного подхода к задаче:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Продажи.Регистратор КАК Регистратор,
| Продажи.СуммаОборот КАК Сумма
|ИЗ
| РегистрНакопления.Продажи.Обороты КАК Продажи
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| ЗНАЧЕНИЕ(Документ.РеализацияТоваровУслуг.ПустаяСсылка),
| Сведения.Значение
|ИЗ
| РегистрСведений.Настройки КАК Сведения";
ТаблицаРезультатов = Запрос.Выполнить().Выгрузить();
// Теперь ТаблицаРезультатов содержит корректные данные без лишних строк
Используя эти методы, вы обеспечите стабильную работу системы и корректность отображаемых данных в ваших отчетах и обработках.