Как получить общее число оборотов по дням в запросе 1С без использования СКД?

Программист 1С v8.3 (Обычные формы) Управленческий учет Сфера услуг, туризм и социальный сектор
← На главную

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

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

Основные ошибки при группировке

В обсуждении на форуме автор столкнулся с несколькими проблемами:

  1. Данные за один день выводились в разных строках.
  2. Не получалось сложить два ресурса ("Узнавший из других источников" и "Пришедший по приглашению") в одну общую сумму.
  3. Возникали синтаксические ошибки при попытке добавить группировку в текст запроса динамически.

Разберем причины этих ошибок. Если запрос возвращает несколько строк за одну дату, это означает, что в выборке присутствуют поля, различающиеся для этих записей, и по ним не произведена группировка. Например, если вы выбираете не только Период, но и Регистратор (документ) или другое измерение, строки не "схлопнутся".

Правильное использование виртуальной таблицы оборотов

Для получения данных по дням лучше всего использовать виртуальную таблицу Обороты. У неё есть параметр "Периодичность". Если установить его в значение День, система сама подготовит поле Период, приведенное к началу дня.

Однако простого выбора виртуальной таблицы иногда недостаточно, если вы хотите гарантированно получить одну строку и просуммировать ресурсы. Рассмотрим правильную структуру запроса.

Шаг 1. Формирование текста запроса

Чтобы данные сгруппировались корректно, необходимо:

  1. Использовать агрегатную функцию СУММА() для числовых полей.
  2. Использовать конструкцию СГРУППИРОВАТЬ ПО для поля даты.
  3. Вычислять итоговую колонку (сумма двух ресурсов) прямо в запросе.

Посмотрим на пример правильного кода:


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

Запрос.Текст = Запрос.Текст + "
    |СГРУППИРОВАТЬ ПО
    |   РаботаОбороты.Период
    |УПОРЯДОЧИТЬ ПО
    |   Период";
    
Запрос.УстановитьПараметр("Дата1", НачалоДня(Дата1));
Запрос.УстановитьПараметр("Дата2", КонецДня(Дата2));

Обратите внимание на важные детали:

Шаг 2. Обход результата и вывод в макет

В исходном коде автора была сложная структура с лишними циклами и созданием промежуточной Структуры. В 1С это излишне. Выборку запроса можно использовать напрямую для заполнения параметров макета.

Оптимизируем код вывода:


Выборка = Запрос.Выполнить().Выбрать();

ТабДок = Новый ТабличныйДокумент;
// ... получение макета и областей (как в исходном коде) ...

Пока Выборка.Следующий() Цикл
    // Заполняем параметры области данными из запроса
    // Имена параметров в макете должны совпадать с псевдонимами в запросе (ИсточникДругой, ИсточникПриглашение и т.д.)
    
    СтрокаОтчета.Параметры.Заполнить(Выборка);
    
    // Если имена отличаются, заполняем вручную:
    // СтрокаОтчета.Параметры.УзнавшийИзДругихИсточников = Выборка.ИсточникДругой;
    // СтрокаОтчета.Параметры.ОбщееКоличество = Выборка.ВсегоЗаДень;
    
    ТабДок.Вывести(СтрокаОтчета);
КонецЦикла;

ТабДок.Показать();

Почему возникала ошибка синтаксиса "ГДЕ"?

При динамическом формировании текста запроса (склейке строк) часто забывают про пробелы. Рассмотрим ошибку автора:


// Ошибка: нет пробела перед ГДЕ и после предыдущей строки
Запрос.Текст = Запрос.Текст + "|ГДЕ ..."; 

Если первая часть запроса заканчивалась на ...КАК РаботаОбороты" (без пробела или переноса строки), то при склейке получалось ...КАК РаботаОборотыГДЕ.... Система не могла распознать ключевые слова. Всегда начинайте новую строку запроса с пробела или символа переноса строки |.

Кроме того, порядок секций в запросе строгий:

  1. ВЫБРАТЬ
  2. ИЗ
  3. ГДЕ
  4. СГРУППИРОВАТЬ ПО
  5. ИМЕЮЩИЕ
  6. УПОРЯДОЧИТЬ ПО
  7. ИТОГИ

Если вы добавляете условие ГДЕ программно, вы должны убедиться, что СГРУППИРОВАТЬ ПО окажется после условия, а не будет затерто или вставлено в неправильное место.

Рекомендация по фильтрации

Для виртуальных таблиц (таких как Обороты) фильтрацию по измерениям (например, по Прод - продавцу) эффективнее накладывать не через ГДЕ, а через параметры самой виртуальной таблицы. Это работает быстрее, так как база данных отсекает лишнее еще до формирования итоговой таблицы.


// Пример параметров виртуальной таблицы:
// 1. Начало периода
// 2. Конец периода
// 3. Периодичность (День)
// 4. Условие (Прод = &Прод)

РегистрНакопления.Работа.Обороты(&Дата1, &Дата2, День, Прод = &Прод)

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

← На главную