Как в запросе 1С 7.7 вывести все элементы справочника с остатками регистра (аналог левого соединения)

Программист 1С v7.7 Управленческий учет Торговля и дистрибуция
← На главную

При разработке отчетов в платформе 1С:Предприятие 7.7 нередко возникает классическая задача: получить список абсолютно всех позиций номенклатуры из справочника и сопоставить им текущие остатки из регистра. Если написать стандартный запрос к остаткам регистра, в результат попадут только те позиции, по которым на момент формирования отчета зафиксирован ненулевой остаток либо были движения за указанный период. Разберем подробнее, как средствами встроенного языка запросов 1С 7.7 реализовать аналог левого соединения (LEFT JOIN) справочника с регистром.

Проанализируем типовой исходный запрос, возвращающий только позиции с остатками:


//{{QUERY(Generate)
|Period from SelBegPeriod till SelEndPeriod;
|Sklad = Register.Tovar.Sklad;
|TOVAR = Register.Tovar.TOVAR;
|KOLICH = Register.Tovar.KOLICH;
|Stoim = Register.Tovar.Stoim;
|Function KolichEnd = EndRest(KOLICH);
|Function StoimEnd = EndRest(Stoim);
|Group Sklad;
|Group TOVAR;
|;

Решение 1. Использование модификатора «Все» (All) в тексте запроса

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

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

  1. Строгий порядок модификаторов: модификатор отключения иерархии Без Групп (Without Groups) должен стоять перед модификатором Все (All).
  2. Русскоязычный синтаксис:
    
    Группировка Товар Без Групп Все;
    // или с сохранением иерархии:
    Группировка Товар Все;
    
  3. Англоязычный синтаксис:
    
    Group TOVAR Without Groups All;
    // или с сохранением иерархии:
    Group TOVAR All;
    

Посмотрим на пример динамического формирования текста запроса с проверкой флага вывода иерархии:


ТекстЗапроса = 
"//{{QUERY(Generate)
|Period from SelBegPeriod till SelEndPeriod;
|Sklad = Register.Tovar.Sklad;
|TOVAR = Register.Tovar.TOVAR;
|KOLICH = Register.Tovar.KOLICH;
|Stoim = Register.Tovar.Stoim;
|Function KolichEnd = EndRest(KOLICH);
|Function StoimEnd = EndRest(Stoim);
|Group Sklad;
|";

Если ФлагБезГрупп = 1 Тогда
    ТекстЗапроса = ТекстЗапроса + "Group TOVAR Without Groups All;";
Иначе
    ТекстЗапроса = ТекстЗапроса + "Group TOVAR All;";
КонецЕсли;

Запрос = СоздатьОбъект("Запрос");
Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
    // Обход результатов запроса
КонецЕсли;

Выясним причину разницы между модификаторами Все и Все ВошедшиеВЗапрос (IncludedInQuery):

  1. Все (All) — платформа перебирает абсолютно все элементы указанного справочника, даже если в регистре по ним никогда не было ни одной записи.
  2. Все ВошедшиеВЗапрос (IncludedInQuery) — выводятся только те позиции, которые фигурировали в движениях регистра за выбранный период, но их конечное сальдо по функциям (например, EndRest) оказалось равным нулю.

Важные ограничения и нюансы модификатора «Все»

Рассмотрим подводные камни, с которыми можно столкнуться при использовании данного механизма:

  1. Декартово произведение группировок: если в запросе выше уровня товара стоит другая группировка (например, Group Sklad;), то при добавлении Group TOVAR All; система выведет полный список товаров для каждого склада. Если у вас 10 складов и 10 000 товаров, выборка будет содержать 100 000 строк.
  2. Производительность: транслятор запросов 1С 7.7 при использовании ключевого слова Все последовательно сканирует всю таблицу справочника. На объемных информационных базах время выполнения запроса может существенно возрасти.
  3. Фильтрация через Condition (Условие): если наложить условие на реквизит справочника или родителя через секцию Condition, строки с нулевыми остатками могут быть отсечены платформой до группировки.

Решение 2. Альтернативный обход справочника с получением остатков через Таблицу Значений

Если в базе данных содержится большой объем номенклатуры или использование модификатора Все приводит к зависанию отчета из-за декартова произведения складов и товаров, оптимально применить двухэтапную обработку данных:

  1. Выполнить запрос к регистру без модификатора Все (выбрать только реальные остатки) и выгрузить итоги в объект ТаблицаЗначений.
  2. Установить индекс по колонке номенклатуры в таблице значений.
  3. Обойти справочник методом ВыбратьЭлементы() и для каждого товара быстро получать остаток из таблицы через НайтиЗначение() или ПолучитьСтрокуПоНомеру().

Посмотрим на пример реализации такого подхода в коде 1С 7.7:


Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса = 
"//{{QUERY(Остатки)
|Период с ВыбНачПериода по ВыбКонПериода;
|Товар = Регистр.Товар.Товар;
|Колич = Регистр.Товар.Колич;
|Функция КонОст = КонОст(Колич);
|Группировка Товар Без Групп;
|";

Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
    ТЗ = СоздатьОбъект("ТаблицаЗначений");
    Запрос.Выгрузить(ТЗ, 0, 0);
    
    Спр = СоздатьОбъект("Справочник.Номенклатура");
    Спр.ВыбратьЭлементы();
    Пока Спр.ПолучитьЭлемент() = 1 Цикл
        Если Спр.ЭтоГруппа() = 1 Тогда
            Продолжить;
        КонецЕсли;
        
        ТекТовар = Спр.ТекущийЭлемент();
        СтрокаТЗ = 0;
        
        Если ТЗ.НайтиЗначение(ТекТовар, СтрокаТЗ, "Товар") = 1 Тогда
            Остаток = ТЗ.ПолучитьЗначение(СтрокаТЗ, "КонОст");
        Иначе
            Остаток = 0;
        КонецЕсли;
        
        // Вывод строки отчета
    КонецЦикла;
КонецЕсли;

Такой подход обеспечивает контролируемую скорость работы на любых объемах данных и гарантирует, что в итоговый отчет попадет вся номенклатура без избыточного дублирования строк.

← На главную