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