При построении запросов в платформе 1С:Предприятие 7.7 начинающие разработчики часто сталкиваются с ситуацией, когда реквизит справочника (например, ДатаСоздания) в выборке оказывается пустым, хотя при обращении только к справочнику данные извлекаются корректно. Проанализируем исходный код, выясним архитектурную причину такого поведения и рассмотрим правильные варианты реализации выборки.
Посмотрим на пример запроса, вызвавшего затруднение:
ТекстЗапроса = "
|Обрабатывать НеПомеченныеНаУдаление;
|ОбрабатыватьДокументы Проведенные;
|Номенклатура = Справочник.Номенклатура.ТекущийЭлемент,
| Документ.ПоступлениеТМЦ.Номенклатура;
|ДатаСозд = Справочник.Номенклатура.ДатаСоздания;
|ДатаПервогоПоступления = Документ.ПоступлениеТМЦ.ДатаДок;
|Группировка Номенклатура;
|Условие (ДатаСоздания > ДатаС);
|Условие ((ТекущаяДата() - ДатаПервогоПоступления) < 31);";
В языке запросов 1С 7.7 перечисление нескольких источников через запятую в секции объявления переменных не создает реляционного соединения таблиц (JOIN), к которому привыкли разработчики в SQL или 1С 8. Механизм запросов 1С 7.7 считывает справочник и табличные части документов как независимые потоки данных, фактически объединяя их (по принципу UNION ALL).
Когда мы объявляем составную переменную Номенклатура, в выборку попадают записи двух типов:
Справочник.Номенклатура.Документ.ПоступлениеТМЦ.Если реквизит ДатаСозд определен только через источник справочника (Справочник.Номенклатура.ДатаСоздания), то для всех строк, пришедших из потока документов, значение этого поля не считывается и остается пустым. Когда платформа сворачивает результат по группировке Номенклатура, несинхронизированное поле теряется или перекрывается пустым значением.
Чтобы реквизит корректно заполнялся из обоих потоков выборки, свяжем его со всеми источниками данных через запятую, точно так же, как это сделано для самой номенклатуры:
|Номенклатура = Справочник.Номенклатура.ТекущийЭлемент,
| Документ.ПоступлениеТМЦ.Номенклатура;
|ДатаСозд = Справочник.Номенклатура.ДатаСоздания,
| Документ.ПоступлениеТМЦ.Номенклатура.ДатаСоздания;
В этом случае платформа считывает реквизит ДатаСоздания и при сканировании справочника, и через реквизит табличной части документа (через точку).
Важное замечание по условиям: обратим внимание на имя переменной. Если поле названо ДатаСозд, то и в блоке фильтрации должно использоваться именно это имя:
|Условие (ДатаСозд > ДатаС);
Если указать Условие (ДатаСоздания > ДатаС), движок 1С 7.7 попытается найти переменную с именем ДатаСоздания в контексте программного модуля, что приведет к неверной фильтрации или полной очистке результата выборки.
Разберем логику задачи: если в отчете требуются только те позиции номенклатуры, по которым было поступление за последний месяц, обращение к таблице Справочник.Номенклатура в тексте запроса является полностью избыточным. Наличие справочника в источниках заставляет платформу перебирать весь справочник целиком, создавая ненужную нагрузку на дисковую подсистему и временные файлы DBF.
Посмотрим, как сделать выборку быстрее и компактнее:
Период С ... По .... Это позволит платформе использовать системный индекс журнала документов по дате вместо полного перебора всех документов базы данных.ТекущаяДата() - ДатаПервогоПоступления < 31 из тела запроса.Итоговый оптимизированный вариант запроса:
ДатаНачала = ТекущаяДата() - 30;
ДатаОкончания = ТекущаяДата();
ТекстЗапроса = "
|Период С ДатаНачала По ДатаОкончания;
|ОбрабатыватьДокументы Проведенные;
|Номенклатура = Документ.ПоступлениеТМЦ.Номенклатура;
|ДатаСозд = Документ.ПоступлениеТМЦ.Номенклатура.ДатаСоздания;
|Док = Документ.ПоступлениеТМЦ.ТекущийДокумент;
|Группировка Номенклатура без групп;
|Группировка Док;
|Условие (ДатаСозд > ДатаС);
|";
Если реквизит ДатаСоздания не участвует в фильтрации Условие(...) внутри самого запроса, его можно не объявлять отдельной строкой в секции переменных. Поле группировки Запрос.Номенклатура возвращает прямую ссылку на элемент справочника. В таком случае мы можем обращаться к реквизиту прямо в коде вывода:
Запрос = СоздатьОбъект("Запрос");
Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
Пока Запрос.Группировка("Номенклатура") = 1 Цикл
// Обращение через точку к элементу справочника
ТекДатаСоздания = Запрос.Номенклатура.ДатаСоздания;
// Вывод данных в таблицу или обработка результата
КонецЦикла;
КонецЕсли;
Такой подход делает текст запроса более простым для чтения и снижает размер формируемых временных таблиц в памяти и на диске.