Почему в запросе 1С 7.7 не выводится реквизит номенклатуры и как это исправить?

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

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

Посмотрим на пример запроса, вызвавшего затруднение:


ТекстЗапроса = " 
|Обрабатывать НеПомеченныеНаУдаление;
|ОбрабатыватьДокументы Проведенные;
|Номенклатура = Справочник.Номенклатура.ТекущийЭлемент,
|               Документ.ПоступлениеТМЦ.Номенклатура;
|ДатаСозд     = Справочник.Номенклатура.ДатаСоздания;  
|ДатаПервогоПоступления = Документ.ПоступлениеТМЦ.ДатаДок;
|Группировка Номенклатура;
|Условие (ДатаСоздания > ДатаС);
|Условие ((ТекущаяДата() - ДатаПервогоПоступления) < 31);";

Причина: как работают составные источники данных в запросах 1С 7.7

В языке запросов 1С 7.7 перечисление нескольких источников через запятую в секции объявления переменных не создает реляционного соединения таблиц (JOIN), к которому привыкли разработчики в SQL или 1С 8. Механизм запросов 1С 7.7 считывает справочник и табличные части документов как независимые потоки данных, фактически объединяя их (по принципу UNION ALL).

Когда мы объявляем составную переменную Номенклатура, в выборку попадают записи двух типов:

  1. Строки, считанные напрямую из таблицы Справочник.Номенклатура.
  2. Строки, считанные из табличной части документа Документ.ПоступлениеТМЦ.

Если реквизит ДатаСозд определен только через источник справочника (Справочник.Номенклатура.ДатаСоздания), то для всех строк, пришедших из потока документов, значение этого поля не считывается и остается пустым. Когда платформа сворачивает результат по группировке Номенклатура, несинхронизированное поле теряется или перекрывается пустым значением.

Решение 1. Явное сопоставление полей в составной переменной

Чтобы реквизит корректно заполнялся из обоих потоков выборки, свяжем его со всеми источниками данных через запятую, точно так же, как это сделано для самой номенклатуры:


|Номенклатура = Справочник.Номенклатура.ТекущийЭлемент,
|               Документ.ПоступлениеТМЦ.Номенклатура;
|ДатаСозд     = Справочник.Номенклатура.ДатаСоздания, 
|               Документ.ПоступлениеТМЦ.Номенклатура.ДатаСоздания;

В этом случае платформа считывает реквизит ДатаСоздания и при сканировании справочника, и через реквизит табличной части документа (через точку).

Важное замечание по условиям: обратим внимание на имя переменной. Если поле названо ДатаСозд, то и в блоке фильтрации должно использоваться именно это имя:

|Условие (ДатаСозд > ДатаС);

Если указать Условие (ДатаСоздания > ДатаС), движок 1С 7.7 попытается найти переменную с именем ДатаСоздания в контексте программного модуля, что приведет к неверной фильтрации или полной очистке результата выборки.

Решение 2. Оптимизация запроса без избыточного объединения со справочником

Разберем логику задачи: если в отчете требуются только те позиции номенклатуры, по которым было поступление за последний месяц, обращение к таблице Справочник.Номенклатура в тексте запроса является полностью избыточным. Наличие справочника в источниках заставляет платформу перебирать весь справочник целиком, создавая ненужную нагрузку на дисковую подсистему и временные файлы DBF.

Посмотрим, как сделать выборку быстрее и компактнее:

  1. Ограничим выборку только документом поступления.
  2. Зададим жесткий временной интервал через директиву Период С ... По .... Это позволит платформе использовать системный индекс журнала документов по дате вместо полного перебора всех документов базы данных.
  3. Уберем медленное вычисление ТекущаяДата() - ДатаПервогоПоступления < 31 из тела запроса.

Итоговый оптимизированный вариант запроса:


ДатаНачала = ТекущаяДата() - 30;
ДатаОкончания = ТекущаяДата();

ТекстЗапроса = "
|Период С ДатаНачала По ДатаОкончания;
|ОбрабатыватьДокументы Проведенные;
|Номенклатура = Документ.ПоступлениеТМЦ.Номенклатура;
|ДатаСозд     = Документ.ПоступлениеТМЦ.Номенклатура.ДатаСоздания;
|Док          = Документ.ПоступлениеТМЦ.ТекущийДокумент;
|Группировка Номенклатура без групп;
|Группировка Док;
|Условие (ДатаСозд > ДатаС);
|";

Решение 3. Получение реквизита при обходе выборки

Если реквизит ДатаСоздания не участвует в фильтрации Условие(...) внутри самого запроса, его можно не объявлять отдельной строкой в секции переменных. Поле группировки Запрос.Номенклатура возвращает прямую ссылку на элемент справочника. В таком случае мы можем обращаться к реквизиту прямо в коде вывода:


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

Такой подход делает текст запроса более простым для чтения и снижает размер формируемых временных таблиц в памяти и на диске.

← На главную