Почему запрос 1С не группирует данные по дням в виртуальных таблицах?

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

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

Разбор основной проблемы: избыточные поля в группировке

Рассмотрим ситуацию: нам нужно получить общее количество покупателей по категориям за каждый день. Мы ожидаем увидеть одну строку за «вчера» и одну строку за «сегодня». Однако система выводит отдельную строку на каждого покупателя, даже если мы используем агрегатную функцию СУММА(). В чем причина?

Основная причина кроется в секции СГРУППИРОВАТЬ ПО. В языке запросов 1С (как и в стандартном SQL) группировка работает по принципу уникальности комбинации всех полей, указанных в этой секции. Если вы добавили в список выбора и в группировку поле Код (или любое другое уникальное поле, например, ссылку на документ или конкретного контрагента), система будет обязана вывести уникальную строку для каждого кода, даже если период совпадает.

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

Типичная ошибка: группировка по ресурсам

Проанализируем фрагмент ошибочного кода, который часто встречается в практике:


ВЫБРАТЬ
    РаботаОбороты.Период,
    СУММА(РаботаОбороты.КоличествоОборот) КАК Кол
ИЗ
    РегистрНакопления.Работа.Обороты(&Дата1, &Дата2, ДЕНЬ, ) КАК РаботаОбороты
СГРУППИРОВАТЬ ПО
    РаботаОбороты.Период,
    РаботаОбороты.КоличествоОборот // ОШИБКА!

В данном примере поле КоличествоОборот является ресурсом. Включая его в СГРУППИРОВАТЬ ПО, вы заставляете систему создавать новую строку каждый раз, когда меняется значение оборота, даже в рамках одного дня. Если в один день было две продажи по 1 шт. и одна по 2 шт., вы получите две строки вместо одной, потому что значения «1» и «2» — разные.

Оптимизация фильтрации: ГДЕ против Параметров виртуальной таблицы

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

Когда мы пишем ГДЕ РаботаОбороты.Прод = &Прод, система сначала выбирает все данные из регистра за период, формирует временный результат в памяти и только потом отсеивает лишнее. Если же мы передадим условие в параметры виртуальной таблицы, СУБД отфильтрует данные еще на этапе чтения с диска.

Рассмотрим, как правильно передавать условия в параметры:


// Неправильно:
ИЗ РегистрНакопления.Работа.Обороты(&Начало, &Конец, ДЕНЬ) КАК Обороты
ГДЕ Обороты.Прод = &Прод

// Правильно:
ИЗ РегистрНакопления.Работа.Обороты(&Начало, &Конец, ДЕНЬ, Прод = &Прод) КАК Обороты

Проблема суммирования и значение NULL

При вычислении итоговых полей (например, «Всего покупателей» как сумма нескольких колонок) мы часто сталкиваемся с тем, что результат внезапно становится пустым. В 1С любая арифметическая операция с типом NULL (например, 10 + NULL) всегда возвращает NULL.

Чтобы избежать этой ситуации, каждое слагаемое необходимо обрабатывать функцией ЕСТЬNULL(). Это гарантирует, что если данных по конкретной категории за день нет, система подставит «0» и корректно вычислит общую сумму.

Пошаговое решение: создание правильного запроса

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

  1. Определяем измерения: Нам нужен только Период с периодичностью ДЕНЬ.
  2. Агрегируем ресурсы: Все показатели (Пришедшие, Рекомендации и т.д.) помещаем в СУММА().
  3. Вычисляем итог: Создаем расчетное поле «Всего», складывая ресурсы внутри ЕСТЬNULL().
  4. Настраиваем фильтры: Условие по продавцу и фильтр по коду (исключение кодов с буквой «Б») выносим в параметры таблицы.

Посмотрим на пример реализации такого запроса:


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

Почему это решение лучше?

Проанализируем преимущества данного подхода:

Дополнительные рекомендации

Если вам все же необходимо видеть детальные данные (например, список покупателей) и при этом иметь итоговую строку по дню, рекомендуем рассмотреть конструкцию ИТОГИ. Она позволяет строить иерархические отчеты, где на верхнем уровне будет сумма за день, а «внутри» — детальные записи.

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


ВЫБРАТЬ
    РаботаОбороты.Период,
    РаботаОбороты.Код,
    РаботаОбороты.ПришедшийПервыйРазОборот
ИЗ
    РегистрНакопления.Работа.Обороты(&Дата1, &Дата2, ДЕНЬ, Прод = &Прод) КАК РаботаОбороты
ИТОГИ
    СУММА(ПришедшийПервыйРазОборот)
ПО
    Период ПЕРИОДАМИ(ДЕНЬ, , )

Такой подход идеально подходит для построения деревьев в обычных или управляемых формах 1С, обеспечивая максимальную информативность данных без потери агрегации.

← На главную