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

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

При разработке отчетов или механизмов заполнения документов в 1С программисты часто сталкиваются с необходимостью обогатить данные табличной части (ТЧ) документа информацией из регистров накопления или сведений. Типичный пример: нам нужно перенести строки из документа ОтчетПроизводстваЗаСмену в документ РеализацияУслуг, но в отчете производства нет цен — их необходимо получить из регистра ЗаказыПокупателей, к которому привязан заказ-основание.

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

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

Рассмотрим стандартную ошибку в логике построения связи. Изначально запрос автора выглядел следующим образом (упрощенная схема связи):


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

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

Когда мы связываем таблицы только по ссылке на заказ, СУБД сопоставляет каждую строку табличной части документа с каждой строкой регистра, сделанной этим заказом. Если в документе было 5 строк и в движениях по заказу было 5 строк, на выходе мы получим 25 строк (декартово произведение). Платформа просто не знает, какую именно строку регистра нужно сопоставить с конкретной строкой табличной части.

Решение 1: Добавление связи по аналитике (Номенклатуре)

Чтобы исключить дублирование строк, нам необходимо наложить дополнительное условие связи по аналитике — в данном случае по полю Номенклатура. СУБД должна понимать, что строку с "Товаром А" из документа нужно соединять только со строкой "Товара А" из регистра.

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


ПО Продукция.Заказ = ЗаказыПокупателей.Регистратор
    И Продукция.Номенклатура = ЗаказыПокупателей.Номенклатура

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

Оптимизация исходного запроса: избавляемся от вложенных таблиц

Помимо неверного условия связи, в исходном коде автора была еще одна серьезная архитектурная ошибка — использование вложенного запроса к физической таблице документа без предварительного отбора. Запрос сначала выбирал абсолютно все строки из всех документов ОтчетПроизводстваЗаСмену в базе данных, соединял их со всеми записями регистра, и только в самом конце (в секции ГДЕ) отсекал лишнее по конкретному документу-параметру &ОПЗС.

Давайте перепишем запрос правильно: обратимся напрямую к табличной части документа и сразу наложим фильтр на конкретную ссылку. Это позволит СУБД мгновенно выбрать только нужные 5 строк и соединить их с регистром, что увеличит скорость выполнения запроса в десятки раз.


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

Спор о поле "НомерСтроки": можно ли связывать по нему?

В процессе обсуждения на форуме возник спор: существует ли в регистрах поле НомерСтроки и можно ли использовать его для связи табличной части и регистра?

Давайте детально разберем этот технический нюанс:

  1. Существует ли это поле? Да, оппоненты на форуме правы. У регистров накопления (РН), бухгалтерии (РБ), расчета (РР) и сведений (РС, если он подчинен регистратору) есть стандартный реквизит НомерСтроки. Он хранит порядковый номер записи в наборе записей по конкретному документу-регистратору.
  2. Можно ли связывать по нему таблицы? Делать это категорически запрещено. Использование поля НомерСтроки для связи ТЧ и регистра — грубейшая ошибка, которая гарантированно приведет к искажению данных в будущем.

Почему нельзя использовать НомерСтроки для связи:

Современное решение: Механизм "Ключей связи"

Что делать, если в табличной части документа легально дублируется номенклатура (например, одна и та же позиция идет с разными характеристиками, ценами или под разные заказы), и нам нужно сопоставить строки один к одному?

Для решения этой проблемы во всех современных типовых конфигурациях 1С (ERP, Комплексная автоматизация, Управление торговлей) используется механизм Ключей связи (или КлючСтроки):

  1. В табличную часть документа добавляется реквизит КлючСвязи с типом Число. При заполнение строк система генерирует для каждой строки свой уникальный идентификатор.
  2. Точно такой же реквизит (или измерение) КлючСвязи добавляется в структуру регистра накопления.
  3. При проведении документа это числовое значение из ТЧ напрямую транслируется в соответствующую запись движения регистра.
  4. В запросах соединение выполняется по двум полям, что обеспечивает абсолютную точность сопоставления:

ПО ДокументТабличнаяЧасть.Ссылка = Регистр.Регистратор
    И ДокументТабличнаяЧасть.КлючСвязи = Регистр.КлючСвязи

Архитектурные Best Practices: где на самом деле должна быть цена?

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

Существует два более правильных подхода к решению данной задачи:

← На главную