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