Как связать Заказ клиента с расчетным документом в запросе 1С без дублирования строк?

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

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

Выясняем причину: почему дублируются строки?

Проанализируем исходный запрос разработчика. В секции ИЗ мы видим следующее объявление таблиц:


ИЗ
    Документ.ЗаказКлиента.Товары КАК ЗаказКлиентаТовары
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК ЗаказКлиента
        ПО ЗаказКлиентаТовары.Ссылка = ЗаказКлиента.Ссылка,
    РегистрНакопления.РасчетыСКлиентамиПоДокументам КАК РасчетыСКлиентамиПоДокументам

Обратите внимание на запятую перед таблицей регистра накопления РегистрНакопления.РасчетыСКлиентамиПоДокументам. В языке запросов 1С (как и в классическом SQL) перечисление таблиц через запятую без указания ключевого слова ПО (ON) означает полное (перекрестное) соединение, или так называемое декартово произведение (CROSS JOIN).

При таком соединении СУБД сопоставляет каждую строку из левой части (табличная часть товаров заказа) со всеми строками из правой части (записи регистра накопления). Если в регистре по данному контрагенту или в целом по базе данных есть 9 различных записей расчетных документов, то каждая строка номенклатуры в вашем заказе продублируется ровно 9 раз. Наша задача — жестко связать конкретный заказ клиента с соответствующими ему записями в регистре.

Шаг 1. Исправляем связь с регистром накопления

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

Вариант А (для старых релизов конфигураций):

В более ранних версиях за связь с заказом отвечало измерение ЗаказКлиента. В этом случае условие связи будет выглядеть так:


ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.РасчетыСКлиентамиПоДокументам КАК РасчетыСКлиентамиПоДокументам
    ПО ЗаказКлиентаТовары.Ссылка = РасчетыСКлиентамиПоДокументам.ЗаказКлиента

Вариант Б (для современных релизов ERP, КА 2, УТ 11):

В современных конфигурациях архитектура взаиморасчетов была унифицирована, и измерение ЗаказКлиента заменили на универсальный реквизит ОбъектРасчетов. Если вы работаете в актуальной базе, связь необходимо строить именно по нему:


ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.РасчетыСКлиентамиПоДокументам КАК РасчетыСКлиентамиПоДокументам
    ПО ЗаказКлиентаТовары.Ссылка = РасчетыСКлиентамиПоДокументам.ОбъектРасчетов

Шаг 2. Учитываем режим «Онлайн-взаиморасчетов» в 1С

Разберем важный архитектурный нюанс, с которым вы неизбежно столкнетесь в современных конфигурациях. Если в вашей базе данных включен режим «Онлайн-взаиморасчеты», то регистр РасчетыСКлиентамиПоДокументам будет абсолютно пустым! Все движения по расчетам с клиентами в этом режиме записываются в совершенно другой регистр — РегистрНакопления.РасчетыСКлиентамиПоСрокам.

Если ваша печатная форма после исправления связей перестала выводить расчетные документы, проверьте настройки системы. Для универсальности и корректной работы в онлайн-режиме запрос следует переписать с обращением к регистру по срокам:


ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.РасчетыСКлиентамиПоСрокам КАК РасчетыСКлиентамиПоСрокам
    ПО ЗаказКлиентаТовары.Ссылка = РасчетыСКлиентамиПоСрокам.ОбъектРасчетов

Шаг 3. Оптимизируем обращение «через точку» и убираем лишние соединения

Давайте посмотрим на еще одну серьезную ошибку производительности в исходном запросе автора. В тексте присутствует явное соединение с шапкой документа:


ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК ЗаказКлиента
    ПО ЗаказКлиентаТовары.Ссылка = ЗаказКлиента.Ссылка

При этом в секции ВЫБРАТЬ автор все равно обращается к реквизитам шапки через поле табличной части: ЗаказКлиентаТовары.Ссылка.Партнер, ЗаказКлиентаТовары.Ссылка.Номер и так далее. Обращение «через точку» от ссылочного поля табличной части заставляет СУБД делать скрытые (неявные) левые соединения под капотом. В итоге база данных выполняет двойную работу: сначала соединяет таблицы явно, а затем делает кучу неявных соединений.

У нас есть два пути решения этой проблемы:

  1. Полностью убрать соединение с шапкой документа. Для простых печатных форм, где обрабатывается небольшое количество документов (например, один документ из параметра &МассивДокументов), платформа 1С оптимизирует разыменование полей «через точку» автоматически. Запрос станет визуально проще.
  2. Использовать поля из уже соединенной таблицы шапки. Это наиболее профессиональный подход, гарантирующий высокую скорость выполнения запроса. Вместо обращений вида ЗаказКлиентаТовары.Ссылка.Реквизит мы будем использовать поля вида ЗаказКлиента.Реквизит.

Давайте применим второй вариант и напишем красивый, оптимизированный и технически правильный запрос.

Итоговый оптимизированный вариант запроса

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


ВЫБРАТЬ
    ЗаказКлиентаТовары.Ссылка КАК Ссылка,
    ЗаказКлиента.Дата КАК ДатаЗаказа,
    ЗаказКлиента.Партнер КАК ЗаказКлиентаПартнер,
    ЗаказКлиента.Номер КАК ЗаказКлиентаНомер,
    ЗаказКлиентаТовары.НомерСтроки КАК НомерСтроки,
    ЗаказКлиентаТовары.Номенклатура КАК Номенклатура,
    ЗаказКлиентаТовары.Характеристика КАК Характеристика,
    ЗаказКлиентаТовары.Количество КАК Количество,
    ЗаказКлиента.ДокументВидДокумента.Наименование КАК ДокументВидДокументаНаименование,
    ЗаказКлиента.ДокументСерия КАК ДокументСерия,
    ЗаказКлиента.ДокументНомер КАК ДокументНомер,
    ЗаказКлиента.ДокументДатаВыдачи КАК ДокументДатаВыдачи,
    ЗаказКлиента.АдресДоставки КАК АдресДоставки,
    ЗаказКлиента.ФИОВодителя КАК ФИОВодителя,
    ЗаказКлиента.СуммаДокумента КАК СуммаДокумента,
    ЗаказКлиента.ДатаОтгрузки КАК ДатаОтгрузки,
    ЗаказКлиента.Организация.Наименование КАК ОрганизацияНаименование,
    ЗаказКлиента.СпособДоставки КАК СпособДоставки,
    РасчетыСКлиентами.РасчетныйДокумент КАК РасчетныйДокумент
ИЗ
    Документ.ЗаказКлиента.Товары КАК ЗаказКлиентаТовары
    
    // Явное и единственное соединение с шапкой документа для получения реквизитов
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК ЗаказКлиента
        ПО ЗаказКлиентаТовары.Ссылка = ЗаказКлиента.Ссылка
        
    // Корректное левое соединение с регистром расчетов (для режима Онлайн-взаиморасчетов)
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.РасчетыСКлиентамиПоСрокам КАК РасчетыСКлиентами
        ПО ЗаказКлиентаТовары.Ссылка = РасчетыСКлиентами.ОбъектРасчетов
ГДЕ
    ЗаказКлиентаТовары.Ссылка В(&МассивДокументов)
ИТОГИ
    СУММА(Количество)
ПО
    Ссылка

Резюмируем: мы заменили перекрестное соединение через запятую на явное ЛЕВОЕ СОЕДИНЕНИЕ, связали таблицы по универсальному измерению ОбъектРасчетов, учли особенности онлайн-взаиморасчетов через таблицу РасчетыСКлиентамиПоСрокам и избавились от избыточных обращений к базе данных через точку от табличной части.

← На главную