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