При разработке отчетов и доработке типовых механизмов (например, отчетов по движениям номенклатуры) часто возникает необходимость дополнить данные регистра накопления информацией из табличной части первичного документа. Однако прямое левое соединение (ЛЕВОЕ СОЕДИНЕНИЕ) виртуальной таблицы оборотов регистра с табличной частью документа нередко приводит к неприятному эффекту: строки начинают дублироваться, а числовые показатели (сумма, количество) искажаются.
Проанализируем конкретную ситуацию: в документе ВозвратТоваровОтКлиента присутствуют две строки с одинаковой номенклатурой, но разными документами реализации. При попытке соединить виртуальную таблицу оборотов регистра ВыручкаИСебестоимостьПродаж с табличной частью документа вместо ожидаемых 2 строк в результате запроса получается 4 строки. Разберем причины такого поведения платформы 1С и рассмотрим правильные варианты построения запроса.
Рассмотрим исходный запрос, вызвавший проблему:
ВЫБРАТЬ
ВыручкаИСебестоимостьПродажОбороты.АналитикаУчетаНоменклатуры,
ВыручкаИСебестоимостьПродажОбороты.ЗаказКлиента,
ВыручкаИСебестоимостьПродажОбороты.Менеджер,
ВыручкаИСебестоимостьПродажОбороты.Склад,
ВыручкаИСебестоимостьПродажОбороты.Соглашение,
ВыручкаИСебестоимостьПродажОбороты.Договор,
ВыручкаИСебестоимостьПродажОбороты.ХозяйственнаяОперация,
ВыручкаИСебестоимостьПродажОбороты.Инициатор,
ВыручкаИСебестоимостьПродажОбороты.КоличествоОборот,
ВыручкаИСебестоимостьПродажОбороты.СуммаВыручкиОборот,
ВыручкаИСебестоимостьПродажОбороты.АналитикаУчетаНоменклатуры.Номенклатура,
ВыручкаИСебестоимостьПродажОбороты.Регистратор,
ВозвратТоваровОтКлиентаТовары.ДокументРеализации
ИЗ
РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(&НачПериода, &КонПериода, Регистратор, ) КАК ВыручкаИСебестоимостьПродажОбороты
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ВозвратТоваровОтКлиента.Товары КАК ВозвратТоваровОтКлиентаТовары
ПО ВыручкаИСебестоимостьПродажОбороты.Регистратор = ВозвратТоваровОтКлиентаТовары.Ссылка
И ВыручкаИСебестоимостьПродажОбороты.АналитикаУчетаНоменклатуры.Номенклатура = ВозвратТоваровОтКлиентаТовары.Номенклатура
И ВыручкаИСебестоимостьПродажОбороты.Инициатор = ВозвратТоваровОтКлиентаТовары.Инициатор
ГДЕ
ВыручкаИСебестоимостьПродажОбороты.Инициатор = &Инициатор
Рассмотрим подробнее архитектурные особенности, приводящие к дублированию строк в данном запросе:
Обороты возвращает свернутые данные по комбинации измерений и регистратора. Если в документе две строки с одинаковой номенклатурой, но в регистре они имеют одинаковый набор измерений (или одинаковый ключ аналитики), то в выборке регистра образуются строки, которые накладываются на каждую соответствующую строку табличной части. Соединение типа «многие ко многим» порождает прямое декартово произведение (2 записи регистра × 2 строки ТЧ = 4 строки).АналитикаУчетаНоменклатуры хранит ссылку на ключ аналитики. Две разные строки документа с одинаковым товаром и характеристиками ссылаются на один и тот же элемент аналитики. Связь по полю Номенклатура не обеспечивает уникальности строки.НомерСтроки табличной части документа отсутствует в виртуальной таблице Обороты регистра накопления, поскольку оно не является измерением регистра. Поэтому сопоставить строки документа и регистра «один в один» по номеру строки в таблице оборотов невозможно.ГДЕ ВыручкаИСебестоимостьПродажОбороты.Инициатор = &Инициатор фильтрует данные уже после выполнения выборки оборотов, вместо того чтобы передать фильтр в параметры самой виртуальной таблицы. Кроме того, обращение через точку АналитикаУчетаНоменклатуры.Номенклатура в секции соединения порождает скрытый LEFT JOIN к таблице ключей аналитики, снижая производительность.Разберем по шагам наиболее универсальный и производительный способ. Чтобы избежать размножения записей, предварительно выгрузим данные табличной части документа во временную таблицу, выполнив группировку по ключевым полям связи:
ВЫБРАТЬ
ВозвратТовары.Ссылка КАК Ссылка,
ВозвратТовары.Номенклатура КАК Номенклатура,
ВозвратТовары.Инициатор КАК Инициатор,
ВозвратТовары.ДокументРеализации КАК ДокументРеализации
ПОМЕСТИТЬ ВТ_ТоварыДокумента
ИЗ
Документ.ВозвратТоваровОтКлиента.Товары КАК ВозвратТовары
СГРУППИРОВАТЬ ПО
ВозвратТовары.Ссылка,
ВозвратТовары.Номенклатура,
ВозвратТовары.Инициатор,
ВозвратТовары.ДокументРеализации
ИНДЕКСИРОВАТЬ ПО
Ссылка,
Номенклатура
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВыручкаОбороты.АналитикаУчетаНоменклатуры КАК АналитикаУчетаНоменклатуры,
ВыручкаОбороты.ЗаказКлиента КАК ЗаказКлиента,
ВыручкаОбороты.Менеджер КАК Менеджер,
ВыручкаОбороты.Склад КАК Склад,
ВыручкаОбороты.Соглашение КАК Соглашение,
ВыручкаОбороты.Договор КАК Договор,
ВыручкаОбороты.ХозяйственнаяОперация КАК ХозяйственнаяОперация,
ВыручкаОбороты.Инициатор КАК Инициатор,
ВыручкаОбороты.КоличествоОборот КАК КоличествоОборот,
ВыручкаОбороты.СуммаВыручкиОборот КАК СуммаВыручкиОборот,
ВыручкаОбороты.Регистратор КАК Регистратор,
ВТ_ТоварыДокумента.ДокументРеализации КАК ДокументРеализации
ИЗ
РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(
&НачПериода,
&КонПериода,
Регистратор,
Инициатор = &Инициатор) КАК ВыручкаОбороты
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ТоварыДокумента КАК ВТ_ТоварыДокумента
ПО ВыручкаОбороты.Регистратор = ВТ_ТоварыДокумента.Ссылка
И ВыручкаОбороты.АналитикаУчетаНоменклатуры.Номенклатура = ВТ_ТоварыДокумента.Номенклатура
И ВыручкаОбороты.Инициатор = ВТ_ТоварыДокумента.Инициатор
Важные моменты:
Обороты, что исключает выборку лишних данных из базы на начальном этапе.СГРУППИРОВАТЬ ПО, исключая повторение связок «Документ - Номенклатура - ДокументРеализации».В типовых конфигурациях 1С (ERP, КА, УТ) при проведении возвратов измерение ЗаказКлиента регистра ВыручкаИСебестоимостьПродаж часто заполняется документом продажи (партией реализации). Если в табличной части возврата указан ДокументРеализации, добавим его в условие связи:
ВЫБРАТЬ
ВыручкаОбороты.АналитикаУчетаНоменклатуры КАК АналитикаУчетаНоменклатуры,
ВыручкаОбороты.ЗаказКлиента КАК ЗаказКлиента,
ВыручкаОбороты.КоличествоОборот КАК КоличествоОборот,
ВыручкаОбороты.СуммаВыручкиОборот КАК СуммаВыручкиОборот,
ВыручкаОбороты.Регистратор КАК Регистратор,
ВозвратТовары.ДокументРеализации КАК ДокументРеализации
ИЗ
РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(
&НачПериода,
&КонПериода,
Регистратор,
Инициатор = &Инициатор) КАК ВыручкаОбороты
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ВозвратТоваровОтКлиента.Товары КАК ВозвратТовары
ПО ВыручкаОбороты.Регистратор = ВозвратТовары.Ссылка
И ВыручкаОбороты.АналитикаУчетаНоменклатуры.Номенклатура = ВозвратТовары.Номенклатура
И ВыручкаОбороты.Инициатор = ВозвратТовары.Инициатор
И ВыручкаОбороты.ЗаказКлиента = ВозвратТовары.ДокументРеализации
Обратите внимание: Данный способ работает только тогда, когда измерение ЗаказКлиента гарантированно заполнено ссылкой на реализацию. Если возврат оформлен без указания документа продажи или поле не заполнено, условие вернет NULL, и данные табличной части не подтянутся.
Если задача заключается в получении детальных данных по конкретному документу-регистратору построчно, вместо виртуальной таблицы Обороты можно обратиться напрямую к таблице движений регистра:
ВЫБРАТЬ
ВыручкаДвижения.Регистратор КАК Регистратор,
ВыручкаДвижения.НомерСтроки КАК НомерСтроки,
ВыручкаДвижения.АналитикаУчетаНоменклатуры КАК АналитикаУчетаНоменклатуры,
ВыручкаДвижения.Количество КАК Количество,
ВыручкаДвижения.СуммаВыручки КАК СуммаВыручки,
ВозвратТовары.ДокументРеализации КАК ДокументРеализации
ИЗ
РегистрНакопления.ВыручкаИСебестоимостьПродаж КАК ВыручкаДвижения
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ВозвратТоваровОтКлиента.Товары КАК ВозвратТовары
ПО ВыручкаДвижения.Регистратор = ВозвратТовары.Ссылка
И ВыручкаДвижения.НомерСтроки = ВозвратТовары.НомерСтроки
ГДЕ
ВыручкаДвижения.Регистратор = &Регистратор
В физической таблице движений поле НомерСтроки доступно, что дает возможность связать строчки движений со строками документа напрямую, если при проведении логика конфигурации записывает движения строго по строкам табличной части.
Если отчет разрабатывается на базе Системы компоновки данных (СКД), наилучшей практикой является отказ от использования ЛЕВОЕ СОЕДИНЕНИЕ в тексте запроса. Создадим два отдельных набора данных:
На вкладке «Связи наборов данных» свяжем эти наборы по полям Регистратор = Ссылка и Номенклатура = Номенклатура. СКД выполнит соединение коллекций в памяти и корректно рассчитает итоги по ресурсам без задвоения количественных и стоимостных показателей.