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