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