При разработке управляемых форм для начальной страницы или рабочих столов часто возникает задача вывода сводных данных по нескольким регистрам накопления — например, суммы выручки и текущих остатков денежных средств в разрезе подразделений. При попытке реализовать такую выборку через динамический список с использованием пакетного запроса и временных таблиц разработчики нередко сталкиваются с ошибкой «Поле не найдено» при открытии формы.
Разберем детально, почему возникает данное поведение, какие ограничения накладывает платформа 1С:Предприятие на запросы динамических списков и какими способами можно правильно решить эту задачу.
Рассмотрим типовой запрос динамического списка, создающий проблему:
ВЫБРАТЬ
ВыручкаИСебестоимостьПродажОбороты.Подразделение,
ВыручкаИСебестоимостьПродажОбороты.СуммаВыручкиОборот
ПОМЕСТИТЬ втВыручкаИСебестоимостьПродаж
ИЗ
РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(&НачалоПериода, &КонецПериода, , ) КАК ВыручкаИСебестоимостьПродажОбороты
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ДенежныеСредстваНаличныеОстатки.Касса.Подразделение КАК Подразделение,
ДенежныеСредстваНаличныеОстатки.СуммаОстаток
ПОМЕСТИТЬ втДенежныеСредстваНаличные
ИЗ
РегистрНакопления.ДенежныеСредстваНаличные.Остатки(, Касса.ОтображатьДС = ИСТИНА) КАК ДенежныеСредстваНаличныеОстатки
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
СтруктураПредприятия.Ссылка,
втВыручкаИСебестоимостьПродаж.СуммаВыручкиОборот,
втДенежныеСредстваНаличные.СуммаОстаток
ИЗ
Справочник.СтруктураПредприятия КАК СтруктураПредприятия
ЛЕВОЕ СОЕДИНЕНИЕ втВыручкаИСебестоимостьПродаж КАК втВыручкаИСебестоимостьПродаж
ПО СтруктураПредприятия.Ссылка = втВыручкаИСебестоимостьПродаж.Подразделение
ЛЕВОЕ СОЕДИНЕНИЕ втДенежныеСредстваНаличные КАК втДенежныеСредстваНаличные
ПО СтруктураПредприятия.Ссылка = втДенежныеСредстваНаличные.Подразделение
ГДЕ
(втВыручкаИСебестоимостьПродаж.СуммаВыручкиОборот > 0
ИЛИ втДенежныеСредстваНаличные.СуммаОстаток > 0)
Выясним причину падения: поддержка пакетных запросов и конструкции ПОМЕСТИТЬ в динамических списках появилась только начиная с версии платформы 8.3.8. Если конфигурация работает с включенным режимом совместимости ниже 8.3.8 (например, Режим совместимости с версией 8.2.16 в старых редакциях УТ 11.0, КА 2.0 или самописных базах), механизм парсинга динамического списка не умеет корректно обрабатывать пакетные выборки и извлекать структуру финальных полей. В результате система считает, что результирующие поля отсутствуют, и генерирует исключение «Поле не найдено».
Если повышение режима совместимости конфигурации невозможно или нежелательно, перепишем запрос без использования пакетного разделения ПОМЕСТИТЬ ... ;, заменив временные таблицы на вложенные запросы в секциях соединения. Вложенные запросы полностью поддерживаются динамическими списками во всех режимах совместимости платформы 1С.
Посмотрим на исправленный текст запроса:
ВЫБРАТЬ
СтруктураПредприятия.Ссылка КАК Подразделение,
ЕСТЬNULL(втВыручка.СуммаВыручкиОборот, 0) КАК СуммаВыручкиОборот,
ЕСТЬNULL(втДС.СуммаОстаток, 0) КАК СуммаОстаток
ИЗ
Справочник.СтруктураПредприятия КАК СтруктураПредприятия
ЛЕВОЕ СОЕДИНЕНИЕ (
ВЫБРАТЬ
ВыручкаОбороты.Подразделение КАК Подразделение,
ВыручкаОбороты.СуммаВыручкиОборот КАК СуммаВыручкиОборот
ИЗ
РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(&НачалоПериода, &КонецПериода, , ) КАК ВыручкаОбороты
) КАК втВыручка
ПО СтруктураПредприятия.Ссылка = втВыручка.Подразделение
ЛЕВОЕ СОЕДИНЕНИЕ (
ВЫБРАТЬ
ДСОстатки.Касса.Подразделение КАК Подразделение,
ДСОстатки.СуммаОстаток КАК СуммаОстаток
ИЗ
РегистрНакопления.ДенежныеСредстваНаличные.Остатки(, Касса.ОтображатьДС = ИСТИНА) КАК ДСОстатки
) КАК втДС
ПО СтруктураПредприятия.Ссылка = втДС.Подразделение
ГДЕ
(ЕСТЬNULL(втВыручка.СуммаВыручкиОборот, 0) > 0
ИЛИ ЕСТЬNULL(втДС.СуммаОстаток, 0) > 0)
Обратим внимание на применение функции ЕСТЬNULL: при левом соединении для подразделений, по которым не было движений или остатков, возвращается значение NULL, что может приводить к некорректным результатам при вычислении условий в секции ГДЕ.
В тексте запроса присутствуют параметры виртуальных таблиц &НачалоПериода и &КонецПериода. Если эти параметры не переданы до первой компоновки списка на сервере, список также не сможет отобразить данные. Проинициализируем параметры в обработчике формы ПриСозданииНаСервере:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Установка параметров динамического списка
Список.Параметры.УстановитьЗначениеПараметра("НачалоПериода", НачалоДня(ТекущаяДатаСеанса()));
Список.Параметры.УстановитьЗначениеПараметра("КонецПериода", КонецДня(ТекущаяДатаСеанса()));
КонецПроцедуры
Проанализируем архитектуру задачи: динамический список предназначен для полинейного постраничного чтения сотен тысяч записей из одной или нескольких таблиц базы данных. Если цель — вывести компактный дашборд или сводку по подразделениям на рабочий стол, динамический список создает избыточную нагрузку, поскольку при каждом обновлении платформа накладывает оборачивающие запросы и рассчитывает порции данных.
Разберем по шагам альтернативный и более устойчивый подход:
СводкаДанных с типом ТаблицаЗначений и колонками Подразделение, СуммаВыручкиОборот, СуммаОстаток.Запрос (где временные таблицы работают абсолютно стабильно в любых версиях платформы и СУБД).ЗначениеВРеквизитФормы.ПодключитьОбработчикОжидания.Посмотрим на реализацию серверной процедуры загрузки данных:
&НаСервере
Процедура ОбновитьСводкуНаСервере()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВыручкаОбороты.Подразделение КАК Подразделение,
| ВыручкаОбороты.СуммаВыручкиОборот КАК СуммаВыручкиОборот
|ПОМЕСТИТЬ втВыручка
|ИЗ
| РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(&НачалоПериода, &КонецПериода, , ) КАК ВыручкаОбороты
|;
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ДСОстатки.Касса.Подразделение КАК Подразделение,
| ДСОстатки.СуммаОстаток КАК СуммаОстаток
|ПОМЕСТИТЬ втДС
|ИЗ
| РегистрНакопления.ДенежныеСредстваНаличные.Остатки(, Касса.ОтображатьДС = ИСТИНА) КАК ДСОстатки
|;
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| СтруктураПредприятия.Ссылка КАК Подразделение,
| ЕСТЬNULL(втВыручка.СуммаВыручкиОборот, 0) КАК СуммаВыручкиОборот,
| ЕСТЬNULL(втДС.СуммаОстаток, 0) КАК СуммаОстаток
|ИЗ
| Справочник.СтруктураПредприятия КАК СтруктураПредприятия
| ЛЕВОЕ СОЕДИНЕНИЕ втВыручка КАК втВыручка
| ПО СтруктураПредприятия.Ссылка = втВыручка.Подразделение
| ЛЕВОЕ СОЕДИНЕНИЕ втДС КАК втДС
| ПО СтруктураПредприятия.Ссылка = втДС.Подразделение
|ГДЕ
| (ЕСТЬNULL(втВыручка.СуммаВыручкиОборот, 0) > 0
| ИЛИ ЕСТЬNULL(втДС.СуммаОстаток, 0) > 0)";
Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(ТекущаяДатаСеанса()));
Запрос.УстановитьПараметр("КонецПериода", КонецДня(ТекущаяДатаСеанса()));
ТаблицаРезультата = Запрос.Выполнить().Выгрузить();
ЗначениеВРеквизитФормы(ТаблицаРезультата, "СводкаДанных");
КонецПроцедуры
Подведем итоги и перечислим важные правила при проектировании запросов динамических списков:
ТекстЗапроса динамического списка допустимы только при режиме совместимости 8.3.8 и выше. Для совместимости с 8.2 следует применять вложенные подзапросы.ИТОГИ и ПЕРВЫЕ в основном теле запроса, так как механизм постраничного считывания платформы самостоятельно управляет выборкой порций данных.