Почему в отчете 1С количество больше, чем в документе, и как это исправить?

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

При разработке отчетов в системе 1С:Предприятие начинающие (а иногда и опытные) разработчики сталкиваются с загадочной ситуацией: запрос выдает количество товара, которое в несколько раз превышает реальные показатели в документах. Это классическая проблема, связанная с логикой работы SQL-соединений. В этой статье мы подробно разберем, почему так происходит, проанализируем механизмы возникновения "лишних" данных и изучим правильные методики написания запросов, которые гарантируют корректность итогов.

Выясним причину: почему данные "размножаются"?

Рассмотрим ситуацию, представленную в исходном коде. Разработчик пытается соединить табличную часть документа ПеремещениеТоваров.Товары с табличными частями двух других документов: ПриходныйОрдерНаТовары.Товары и РасходныйОрдерНаТовары.Товары. Проблема заключается в возникновении так называемого декартова произведения (Cartesian Product).

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

  1. Система берет первую строку из ПеремещениеТоваров.
  2. Пытается найти все соответствующие строки в табличной части ПриходныйОрдерНаТовары. Если там 3 строки, то одна строка перемещения превращается в три.
  3. Затем результат соединяется с РасходныйОрдерНаТовары. Если и там 3 строки, то наши уже имеющиеся 3 строки умножаются еще на 3. В итоге вместо одной строки мы получаем девять.

Когда в секции выбора мы используем агрегатную функцию СУММА(Количество), 1С честно суммирует количество по всем этим девяти строкам. В результате мы видим число, завышенное в разы. Как отметили участники обсуждения, количество строк в итоговой выборке будет равно произведению количества строк во всех соединяемых табличных частях.

Анализируем типичную ошибку в коде

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


ВЫБРАТЬ
    ПеремещениеТоваровТовары.Ссылка КАК Перемещение,
    СУММА(ПеремещениеТоваровТовары.Количество) КАК КоличествоВПеремещении,
    ПриходныйОрдерНаТоварыТовары.Ссылка КАК Приходник
ИЗ
    Документ.ПеремещениеТоваров.Товары КАК ПеремещениеТоваровТовары
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПриходныйОрдерНаТовары.Товары КАК ПриходныйОрдерНаТоварыТовары
        ПО ПеремещениеТоваровТовары.Ссылка = ПриходныйОрдерНаТоварыТовары.Ссылка.ДокументПеремещения

Здесь допущена критическая ошибка: соединение идет ТабличнаяЧасть к ТабличнойЧасти. Если ваша задача — просто вывести ссылку на ордер, соединяться нужно не с таблицей Товары, а с самой таблицей документа (шапкой).

Решение №1: Использование временных таблиц (Лучшая практика)

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

Шаг 1. Подготовка агрегированных данных. Создадим временную таблицу, где посчитаем общую сумму по каждому документу перемещения.


ВЫБРАТЬ
    ПеремещениеТоваровТовары.Ссылка КАК Ссылка,
    СУММА(ПеремещениеТоваровТовары.Количество) КАК КоличествоИтог
ПОМЕСТИТЬ ВТ_ДанныеПеремещений
ИЗ
    Документ.ПеремещениеТоваров.Товары КАК ПеремещениеТоваровТовары
ГДЕ
    ПеремещениеТоваровТовары.Ссылка.Проведен
    И ПеремещениеТоваровТовары.Ссылка.Дата МЕЖДУ &ДатаНачала И &ДатаКонца
СГРУППИРОВАТЬ ПО
    ПеремещениеТоваровТовары.Ссылка
;

Шаг 2. Соединение с шапками связанных документов. Теперь, когда у нас есть уникальные ссылки на перемещения с уже посчитанным итогом, мы можем безопасно присоединить к ним ордера. Важно соединяться именно с основной таблицей документа (Документ.ПриходныйОрдерНаТовары), а не с его товарами.


ВЫБРАТЬ
    ВТ_ДанныеПеремещений.Ссылка КАК Перемещение,
    ВТ_ДанныеПеремещений.КоличествоИтог КАК Количество,
    ПриходныйОрдер.Ссылка КАК ПриходныйОрдер,
    РасходныйОрдер.Ссылка КАК РасходныйОрдер
ИЗ
    ВТ_ДанныеПеремещений КАК ВТ_ДанныеПеремещений
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПриходныйОрдерНаТовары КАК ПриходныйОрдер
        ПО ВТ_ДанныеПеремещений.Ссылка = ПриходныйОрдер.ДокументОснование
            И ПриходныйОрдер.Проведен
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.РасходныйОрдерНаТовары КАК РасходныйОрдер
        ПО ВТ_ДанныеПеремещений.Ссылка = РасходныйОрдер.ДокументОснование
            И РасходныйОрдер.Проведен

Решение №2: Использование агрегатной функции МАКСИМУМ

Если по какой-то причине вы не хотите использовать временные таблицы (хотя это не рекомендуется), можно прибегнуть к хитрости с группировкой. Если мы знаем, что количество в перемещении во всех строках после соединения дублируется, мы можем использовать функцию МАКСИМУМ вместо СУММА. Однако это сработает только если вы предварительно сгруппировали данные самого перемещения.

Важный нюанс: Как справедливо замечено в обсуждении, если просто написать МАКСИМУМ(Количество) в исходном запросе автора, система выберет самое большое число из строк табличной части, а не сумму всех строк документа. Поэтому этот метод применим только к уже рассчитанным итогам.

Оптимизация: Индексы и производительность

Проанализируем еще один важный аспект, упомянутый в теме — проверку признака Проведен. В 1С поле Проведен не является индексированным по умолчанию. Когда мы используем его в условии соединения ПО, СУБД выполняет сканирование таблиц, что на больших объемах данных приведет к существенным задержкам.

Рекомендации по производительности:

  1. Всегда старайтесь выносить условия по дате и складам в первый запрос пакета (при формировании временной таблицы).
  2. Вместо соединения по Проведен = ИСТИНА, лучше предварительно отобрать ссылки проведенных документов во временную таблицу.
  3. Используйте индексацию временных таблиц по полю Ссылка, если планируете дальнейшие соединения с ними.

Работа с критериями отбора

Если ваша задача — просто вывести список связанных документов без сложной логики суммирования ТЧ, проанализируем возможность использования Критериев отбора (например, СвязанныеДокументы). В типовых конфигурациях, таких как УТ 10.3, этот механизм оптимизирован для построения дерева подчиненности. Это работает быстрее, чем "самописные" левые соединения, так как платформа использует внутренние индексные таблицы для связи объектов.

Подведем итоги

Чтобы избежать "размножения" количества в отчетах, придерживайтесь следующих правил:

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

← На главную