При разработке сложных отчетов или обработок на языке запросов 1С программисты часто сталкиваются с ситуацией, когда результат запроса содержит гораздо больше строк, чем ожидалось. Особенно остро эта проблема проявляется при соединении табличных частей различных документов. В этой статье мы подробно разберем, почему возникают дубли, и как правильно спроектировать запрос, чтобы получить корректные данные.
Рассмотрим стандартную ситуацию: нам нужно сопоставить данные из документа «Комплектация номенклатуры» и документа «Поступление товаров и услуг». Если в табличной части «Комплектующие» одна и та же номенклатура встречается дважды (например, с разными характеристиками или просто разными строками), и в документе поступления эта же номенклатура также указана несколько раз, при обычном ЛЕВОМ или ПОЛНОМ СОЕДИНЕНИИ по полю Номенклатура система произведет перекрестное умножение строк.
Проанализируем пример: если слева 2 строки с товаром «Алюминий», а справа 3 строки с тем же товаром, в результате соединения мы получим 6 строк (2 * 3). Это и есть классическое «Декартово произведение», которое искажает итоговые суммы и количество позиций.
Наиболее эффективным и правильным с точки зрения производительности методом является предварительная подготовка данных. Вместо того чтобы соединять «сырые» таблицы документов, мы сначала сгруппируем их по ключевым полям во временных таблицах.
Рассмотрим по шагам реализацию этого подхода.
Шаг 1. Подготовка данных по поступлению во временной таблице
Сначала выберем данные из поступления товаров, сгруппировав их по номенклатуре в разрезе документа-основания. Это позволит нам получить уникальные строки для каждой позиции номенклатуры в конкретном документе.
ВЫБРАТЬ
РеализацияТоваровУслуг.ДокПост КАК ДокПост,
ПоступлениеТоваровУслугТовары.Ссылка КАК Ссылка,
ПоступлениеТоваровУслугТовары.Номенклатура КАК Номенклатура1,
СУММА(ПоступлениеТоваровУслугТовары.Количество) КАК Количество1
ПОМЕСТИТЬ ВтПоступления
ИЗ
Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТоваровУслугТовары
ПО РеализацияТоваровУслуг.ДокПост = ПоступлениеТоваровУслугТовары.Ссылка
ГДЕ
РеализацияТоваровУслуг.Проведен = ИСТИНА
СГРУППИРОВАТЬ ПО
РеализацияТоваровУслуг.ДокПост,
ПоступлениеТоваровУслугТовары.Ссылка,
ПоступлениеТоваровУслугТовары.Номенклатура
;
Шаг 2. Подготовка данных по комплектации
Аналогичным образом подготовим данные из документа комплектации. Обратите внимание, что мы используем СГРУППИРОВАТЬ ПО, чтобы «схлопнуть» возможные дубли номенклатуры внутри одного документа.
ВЫБРАТЬ
РеализацияТоваровУслуг.РасшЛом_ДокКомплектации КАК РасшЛом_ДокКомплектации,
РеализацияТоваровУслуг.ДокПост КАК ДокПост,
КомплектацияНоменклатурыКомплектующие.Номенклатура КАК Номенклатура,
СУММА(КомплектацияНоменклатурыКомплектующие.Количество) КАК Количество
ПОМЕСТИТЬ ВтКомплектующие
ИЗ
Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
ЛЕВОЕ СОЕДИНЕНИЕ Документ.КомплектацияНоменклатуры.Комплектующие КАК КомплектацияНоменклатурыКомплектующие
ПО РеализацияТоваровУслуг.РасшЛом_ДокКомплектации = КомплектацияНоменклатурыКомплектующие.Ссылка
ГДЕ
РеализацияТоваровУслуг.Проведен = ИСТИНА
СГРУППИРОВАТЬ ПО
РеализацияТоваровУслуг.РасшЛом_ДокКомплектации,
РеализацияТоваровУслуг.ДокПост,
КомплектацияНоменклатурыКомплектующие.Номенклатура
;
Шаг 3. Финальное соединение подготовленных данных
Теперь, когда обе таблицы содержат уникальные наборы «Документ + Номенклатура», мы можем выполнить ПОЛНОЕ СОЕДИНЕНИЕ. Это гарантирует, что каждая позиция из комплектации найдет свою пару в поступлении (или останется одна, если соответствия нет), и при этом количество строк не увеличится из-за случайных совпадений.
ВЫБРАТЬ
ВтКомплектующие.РасшЛом_ДокКомплектации КАК РасшЛом_ДокКомплектации,
ВтКомплектующие.Номенклатура КАК Номенклатура,
ВтКомплектующие.Количество КАК Количество,
ВтПоступления.Номенклатура1 КАК Номенклатура1,
ВтПоступления.Количество1 КАК Количество1
ИЗ
ВтКомплектующие КАК ВтКомплектующие
ПОЛНОЕ СОЕДИНЕНИЕ ВтПоступления КАК ВтПоступления
ПО ВтКомплектующие.Номенклатура = ВтПоступления.Номенклатура1
И ВтКомплектующие.ДокПост = ВтПоступления.Ссылка
УПОРЯДОЧИТЬ ПО
РасшЛом_ДокКомплектации,
Номенклатура
Разберем подробнее несколько нюансов, которые сделают ваш запрос профессиональным и отказоустойчивым.
ПОЛНОГО СОЕДИНЕНИЯ поля из одной таблицы могут быть пустыми, если данных нет в другой. Чтобы в колонке «Номенклатура» всегда было название, используйте функцию ЕСТЬNULL(): ЕСТЬNULL(ВтКомплектующие.Номенклатура, ВтПоступления.Номенклатура1) КАК НоменклатураОбщая.ПО и в секцию СГРУППИРОВАТЬ ПО. В противном случае вы получите неверную детализацию.Проанализируем ситуацию, когда ПОЛНОЕ СОЕДИНЕНИЕ работает медленно. В 1С существует практика замены такого соединения на конструкцию ОБЪЕДИНИТЬ ВСЕ с последующей группировкой.
Схема метода выглядит так:
КоличествоКомплектация, а для колонки КоличествоПоступление ставим 0.ОБЪЕДИНИТЬ ВСЕ выбираем данные из второй таблицы, где КоличествоКомплектация будет 0, а КоличествоПоступление заполнено.СУММА по количественным полям с группировкой по номенклатуре.Этот способ часто оказывается более производительным на платформе 8.3 и полностью исключает риск появления дублей, так как в принципе не использует механизм соединений таблиц.
Заключение: Чтобы избежать дублей, всегда старайтесь привести объединяемые таблицы к плоскому виду, где ключевые поля (например, Номенклатура) являются уникальными в пределах разреза (Документа). Группировка во временных таблицах — это стандарт «золотого сечения» в разработке на 1С.