В практике разработки на платформе 1С:Предприятие 8 часто возникает ситуация, когда один и тот же запрос к виртуальной таблице оборотов регистра накопления или бухгалтерии выдает разные результаты при изменении одного лишь параметра — Периодичность. На первый взгляд это кажется нелогичным: ведь состав данных в регистре не меняется, меняется только детализация. Однако за этим поведением стоит строгая математическая и системная логика работы SQL-запросов, генерируемых платформой.
В этой статье мы подробно разберем, почему итоговые суммы могут «плавать» при переключении между периодичностью Период, Регистратор или при ее отсутствии, и выясним, как правильно накладывать условия, чтобы всегда получать корректные данные.
Для начала проанализируем, как система 1С строит виртуальную таблицу. Важно понимать, что в базе данных SQL физически не существует таблицы «Обороты». Есть таблицы движений и таблицы итогов. Когда мы обращаемся к виртуальной таблице в запросе, платформа «на лету» формирует вложенный запрос (subquery), который агрегирует данные.
Рассмотрим два сценария формирования выборки:
СуммаОборот) за указанный период в разрезе выбранных измерений. Если измерения не выбраны вовсе, результатом будет одна-единственная строка с общим итогом.Сам по себе этот механизм не меняет итоговую сумму. Сумма всех строк «по регистраторам» должна быть равна одной строке «общего итога». Различия начинаются в тот момент, когда в игру вступает секция ГДЕ.
Разберем ключевую причину проблемы. В системе 1С условия, прописанные в секции ГДЕ, применяются к результату работы виртуальной таблицы. Это критически важный момент для понимания. Это работает аналогично оператору HAVING в стандартном SQL.
Представим ситуацию: у нас есть два документа.
Проанализируем, что произойдет, если мы напишем запрос с условием ГДЕ СуммаОборот > 0.
Вариант А: Периодичность не указана.
Виртуальная таблица сначала вычислит общий итог: 100 - 20 = 80. Затем сработает условие ГДЕ 80 > 0. Условие истинно, и запрос вернет нам результат: 80.
Вариант Б: Периодичность установлена в значение Регистратор.
Виртуальная таблица выдаст две строки:
ГДЕ к каждой строке отдельно. Строка 1 проходит фильтр (100 > 0), а строка 2 — отсекается (-20 не больше 0). В итоге отчет покажет нам сумму: 100.
Таким образом, мы видим разный результат (80 против 100) на одних и тех же данных. Причина — фильтрация сгруппированных данных по ресурсу.
Для решения подобных проблем крайне важно разделять, где именно накладывается фильтр. Рассмотрим пример правильного и неправильного написания запроса.
Неправильный подход (фильтрация результата):
ВЫБРАТЬ
РегистрОбороты.СуммаОборот КАК Сумма
ИЗ
РегистрНакопления.Продажи.Обороты(&Начало, &Конец, Регистратор, ) КАК РегистрОбороты
ГДЕ
РегистрОбороты.СуммаОборот > 0
Правильный подход (фильтрация исходных данных):
Если нам нужно отсечь определенные движения еще до их суммирования (например, только движения с видом «Приход»), условие нужно передавать в параметры самой виртуальной таблицы. Это четвертый параметр функции Обороты.
ВЫБРАТЬ
РегистрОбороты.СуммаОборот КАК Сумма
ИЗ
РегистрНакопления.Продажи.Обороты(&Начало, &Конец, Регистратор, ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)) КАК РегистрОбороты
В этом случае платформа наложит фильтр непосредственно на записи физической таблицы ВЫБРАТЬ ... ИЗ ... WHERE, и только после этого начнет их группировать. Результат будет математически предсказуемым независимо от выбранной периодичности.
Выясним еще одну важную деталь — использование значения Авто в поле периодичности. Многие разработчики полагают, что это значение «безопасно», но это не так. Периодичность Авто подстраивается под те поля, которые выбраны в секции ВЫБРАТЬ.
Если вы добавите в выборку поле Регистратор или ПериодСекунда, таблица автоматически перейдет в режим детализации по записям. Если же этих полей нет, она вернет общие итоги. Если при этом в запросе присутствует секция ГДЕ с фильтром по суммам, простое добавление поля Регистратор в список колонок может внезапно изменить итоговую сумму в подвале отчета, что часто ставит пользователей в тупик.
Подведем итог и сформулируем правила, которые помогут избежать ошибок при работе с виртуальными таблицами:
ГДЕ должна использоваться только в редких случаях для фильтрации уже рассчитанных итогов, понимая последствия группировки.Регистратор, всегда сравнивайте общую сумму с запросом без периодичности. Если они не совпадают — ваше условие ГДЕ работает некорректно.Граница не решила проблему (и это логично, так как она влияет на интервал, а не на группировку), хорошим тоном считается использование объекта Граница(КонецПериода, ВидГраницы.Включая) для исключения проблем с попаданием документов, записанных ровно в последнюю секунду периода.Проанализировав данную ситуацию, мы видим, что «странное» поведение запроса — это не ошибка платформы, а стандартное поведение реляционных баз данных при работе с агрегатами. Понимание того, в какой момент 1С выполняет группировку, а в какой — фильтрацию, позволяет писать надежные и быстрые отчеты.