Почему запрос к виртуальной таблице оборотов выдает дублирующиеся строки и как это исправить?

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

При работе с регистрами накопления в современных конфигурациях на платформе 1С:Предприятие 8.3 (таких как ERP 2.5, Комплексная автоматизация 2 или Управление торговлей 11) разработчики часто сталкиваются с ситуацией, когда простейший запрос к виртуальной таблице Обороты возвращает гораздо больше строк, чем ожидается. Например, вместо одной строки с итоговым количеством по номенклатуре за месяц, запрос выдает несколько строк, где количество разбито на части.

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

Суть проблемы: лишние строки в результате запроса

Представим ситуацию, когда нам необходимо соединить таблицу с периодами и данными по оборудованию с данными о продажах из регистра накопления ВыручкаИСебестоимостьПродаж. Рассмотрим фрагмент кода, который вызывает затруднения:


ВЫБРАТЬ РАЗРЕШЕННЫЕ
    ПродажиСпериодамиИЗаявками.Оборудование КАК Оборудование,
    ВЫБОР
        КОГДА &Периодичность = "Месяц"
            ТОГДА ЕСТЬNULL(ВыручкаИСебестоимостьПродажОборотыМесяц.КоличествоОборот, 0)
        ИНАЧЕ 0
    КОНЕЦ КАК Количество,
    ПродажиСпериодамиИЗаявками.НачалоПер КАК Период
ИЗ
    ПродажиСпериодамиИЗаявками КАК ПродажиСпериодамиИЗаявками
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(&НачалоПериода, &КонецПериода, Месяц, ) КАК ВыручкаИСебестоимостьПродажОборотыМесяц
        ПО (ПродажиСпериодамиИЗаявками.Оборудование = ВыручкаИСебестоимостьПродажОборотыМесяц.АналитикаУчетаНоменклатуры.Номенклатура)
            И (ПродажиСпериодамиИЗаявками.НачалоПер = ВыручкаИСебестоимостьПродажОборотыМесяц.Период)

Проблема заключается в том, что даже если за февраль была продажа всего 2 штук одного товара, виртуальная таблица Обороты может вернуть две отдельные записи по 1 штуке. В итоге при соединении с основной таблицей количество строк в результате увеличивается, а данные выглядят "раздутыми".

Анализ причины: особенности измерения АналитикаУчетаНоменклатуры

Выясним причину, почему виртуальная таблица не сворачивает данные до одной строки. В регистре ВыручкаИСебестоимостьПродаж основным измерением является АналитикаУчетаНоменклатуры. Это не просто ссылка на справочник Номенклатура, а ссылка на вспомогательный справочник (ключи аналитики).

Важные особенности хранения данных:

  1. Ключи аналитики: Один и тот же элемент справочника Номенклатура может иметь несколько ключей аналитики, если он продавался с разными характеристиками, сериями или через разные склады (в зависимости от настроек системы).
  2. Автоматическая группировка: Виртуальная таблица Обороты автоматически группирует данные только по измерениям регистра. Если в запросе вы обращаетесь к реквизиту измерения через точку (например, АналитикаУчетаНоменклатуры.Номенклатура), платформа все равно получает данные в разрезе самого измерения АналитикаУчетаНоменклатуры.
  3. Разные значения измерений: Если в феврале были продажи одного и того же товара, но с разными характеристиками, в виртуальной таблице будет две строки, так как у них разные значения измерения АналитикаУчетаНоменклатуры.

Проанализируем пример из обсуждения: при выборе поля КоличествоОборот и АналитикаУчетаНоменклатуры.Номенклатура система выдает две строки. Но если мы добавим в запрос Регистратор или само поле АналитикаУчетаНоменклатуры, мы увидим, что записи на самом деле разные на уровне ключей аналитики или документов-оснований.

Решение №1: Явная группировка в запросе

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

Рассмотрим исправленный вариант запроса:


ВЫБРАТЬ
    ПродажиСпериодамиИЗаявками.Оборудование КАК Оборудование,
    СУММА(ВЫБОР
        КОГДА &Периодичность = "Месяц"
            ТОГДА ЕСТЬNULL(ПродажиОбороты.КоличествоОборот, 0)
        ИНАЧЕ 0
    КОНЕЦ) КАК Количество,
    ПродажиСпериодамиИЗаявками.НачалоПер КАК Период
ИЗ
    ПродажиСпериодамиИЗаявками КАК ПродажиСпериодамиИЗаявками
        ЛЕВОЕ СОЕДИНЕНИЕ (
            ВЫБРАТЬ
                ОборотыРегистра.АналитикаУчетаНоменклатуры.Номенклатура КАК Номенклатура,
                ОборотыРегистра.Период КАК Период,
                СУММА(ОборотыРегистра.КоличествоОборот) КАК КоличествоОборот
            ИЗ
                РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(&НачалоПериода, &КонецПериода, Месяц, ) КАК ОборотыРегистра
            СГРУППИРОВАТЬ ПО
                ОборотыРегистра.АналитикаУчетаНоменклатуры.Номенклатура,
                ОборотыРегистра.Период
        ) КАК ПродажиОбороты
        ПО ПродажиСпериодамиИЗаявками.Оборудование = ПродажиОбороты.Номенклатура
            И ПродажиСпериодамиИЗаявками.НачалоПер = ПродажиОбороты.Период
СГРУППИРОВАТЬ ПО
    ПродажиСпериодамиИЗаявками.Оборудование,
    ПродажиСпериодамиИЗаявками.НачалоПер

В данном примере мы сначала во вложенном запросе (или лучше использовать Временную Таблицу) сворачиваем данные регистра до нужной нам аналитики (Номенклатура) и периода, а уже затем выполняем соединение.

Решение №2: Использование параметров виртуальной таблицы

Посмотрим, как можно оптимизировать выборку данных еще на этапе обращения к виртуальной таблице. Если нам нужны данные по конкретной номенклатуре, мы можем наложить отбор прямо в параметрах виртуальной таблицы. Это значительно ускорит выполнение запроса в большой базе.

Рассмотрим синтаксис:

РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(&Начало, &Конец, Месяц, АналитикаУчетаНоменклатуры.Номенклатура = &Номенклатура)

Однако помните, что даже с таким отбором, если у этой номенклатуры было несколько ключей аналитики (например, разные склады в одном периоде), виртуальная таблица все равно вернет несколько строк. Поэтому группировка остается обязательной.

Оптимизация производительности: избавляемся от лишних соединений

В исходном запросе автора использовалось четыре ЛЕВОЕ СОЕДИНЕНИЕ с одной и той же таблицей, но с разной периодичностью (Неделя, Месяц, Квартал, Год). Это крайне неэффективный подход, так как СУБД приходится выполнять огромный объем лишней работы.

Проанализируем, как сделать лучше:

  1. Динамическое формирование текста запроса: В коде на встроенном языке 1С определим выбранную пользователем периодичность и подставим в текст запроса только одну нужную таблицу.
  2. Использование временных таблиц: Сначала выгрузим данные основной таблицы ПродажиСпериодамиИЗаявками во временную таблицу. Затем, в зависимости от условий, выгрузим обороты регистра тоже во временную таблицу (с предварительной группировкой), и только в финальном запросе соединим две компактные временные таблицы.
  3. Параметр Периодичность: Вместо сложной конструкции ВЫБОР КОГДА в тексте запроса, используйте возможность платформы передавать периодичность как параметр в виртуальную таблицу, если вы строите запрос динамически.

Важные рекомендации по отладке

Если вы видите, что запрос выдает "дичь", выполните следующие шаги для поиска ошибки:

Таким образом, мы выяснили, что основная причина появления лишних записей при работе с регистром ВыручкаИСебестоимостьПродаж — это детальность хранения данных в разрезе ключей аналитики. Всегда используйте явную группировку по реквизитам измерений, к которым вы обращаетесь через точку, чтобы получить корректные итоговые показатели.

← На главную