Динамические списки в 1С:Предприятие — это мощный инструмент для отображения данных с возможностью фильтрации, сортировки и группировки. Однако стандартные запросы, формируемые платформой автоматически, не всегда соответствуют бизнес-требованиям. Часто возникает необходимость программно модифицировать запрос — добавить дополнительные условия, изменить структуру выборки или оптимизировать производительность. В этой статье разберём все доступные способы изменения запросов динамических списков, от простых до сложных, с примерами кода и типичными ошибками.

Особенность динамических списков заключается в том, что их запрос формируется на этапе выполнения, а не на этапе разработки. Это означает, что программист может вмешаться в процесс построения запроса через обработчики событий или напрямую через свойства объекта. Но здесь кроются и подводные камни: некорректные изменения могут привести к ошибкам выполнения, потере данных или даже зависанию клиентского приложения. Поэтому важно понимать, какие именно части запроса можно модифицировать, а какие трогать не рекомендуется.

Статья будет полезна как начинающим разработчикам , так и опытным программистам, которые хотят систематизировать знания по работе с динамическими списками. Мы рассмотрим:

  • 🔹 Основные события, в которых можно изменять запрос (ПриСозданииНаСервере, ПередЗапросомНаСервере и др.)
  • 🔹 Способы модификации текста запроса и параметров
  • 🔹 Работа с виртуальными таблицами и временными таблицами
  • 🔹 Типичные ошибки и как их избежать

1. Архитектура динамического списка: где формируется запрос?

Прежде чем изменять запрос, нужно понять, как он формируется. Динамический список в состоит из нескольких ключевых компонентов:

  1. Источник данных — объект, который определяет, откуда берутся данные (справочник, документ, регистр и т.д.).
  2. Запрос — текст на языке , который выполняется на сервере для получения данных.
  3. Параметры запроса — значения, которые подставляются в запрос при его выполнении (например, отборы по дате или организации).
  4. Результирующая таблица — данные, возвращённые запросом и отображаемые в списке.

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

  • 📌 На этапе создания списка — через обработчик ПриСозданииНаСервере.
  • 📌 Перед выполнением запроса — через обработчик ПередЗапросомНаСервере.
  • 📌 После выполнения запроса — через обработчик ПриПолученииДанныхНаСервере (здесь уже нельзя изменить запрос, но можно модифицировать результаты).

Самый распространённый способ — использование события ПередЗапросомНаСервере, так как оно позволяет изменить текст запроса и его параметры непосредственно перед выполнением. Однако в некоторых случаях (например, при работе с виртуальными таблицами) может потребоваться более раннее вмешательство.

📊 Какой способ модификации запросов вы используете чаще?
Через ПередЗапросомНаСервере
Через ПриСозданииНаСервере
Меняю источник данных
Использую временные таблицы
Другой способ

2. Изменение запроса через событие ПередЗапросомНаСервере

Событие ПередЗапросомНаСервере — это основной инструмент для модификации запроса динамического списка. Оно вызывается на сервере непосредственно перед выполнением запроса и позволяет:

  • 🔧 Изменить текст запроса (Запрос.Текст).
  • 🔧 Добавить или изменить параметры запроса (Запрос.Параметры).
  • 🔧 Установить дополнительные свойства запроса (например, Запрос.УстановитьПараметр()).

Пример простого изменения запроса — добавление дополнительного условия в секцию ГДЕ:

Процедура ДинамическийСписокПередЗапросомНаСервере(Элемент, Запрос, ПараметрыЗапроса) Экспорт

// Добавляем условие по организации

Если НЕ ЗначениеЗаполнено(ПараметрыЗапроса.Организация) Тогда

Организация = Справочники.Организации.ТекущаяОрганизация();

Запрос.Текст = СтрЗаменить(Запрос.Текст, "ГДЕ", "ГДЕ Ссылка.Организация = &Организация И");

Запрос.УстановитьПараметр("Организация", Организация);

КонецЕсли;

КонецПроцедуры

Например, если в исходном запросе нет секции ГДЕ, то замена по слову "ГДЕ" приведёт к ошибке. В таких случаях лучше использовать функцию Найти() или СтрНайти() для проверки наличия подстроки.

Ещё один важный момент — параметры запроса. Если вы добавляете новый параметр (как &Организация в примере выше), обязательно убедитесь, что он установлен через УстановитьПараметр(). Иначе при выполнении запроса возникнет ошибка "Неопределённый параметр".

Правильно ли определена секция ГДЕ/ИМЕЮЩИЕ?

Установлены ли все параметры запроса?

Не нарушена ли структура запроса (отсутствуют лискобки, запятые)?

Протестировано ли изменение на небольшом объёме данных?

-->

3. Работа с виртуальными таблицами в динамических списках

Виртуальные таблицы (например, РегистрНакопления.ОстаткиИОбороты) часто используются в динамических списках для получения агрегированных данных. Однако их запрос формируется по особым правилам, и его модификация имеет нюансы.

Основная проблема заключается в том, что виртуальные таблицы не поддерживают произвольные условия в секции ГДЕ. Например, нельзя просто добавить условие по полю, которого нет в структуре виртуальной таблицы. В таких случаях приходится:

  • 🛠 Использовать временные таблицы для предварительной выборки.
  • 🛠 Модифицировать источник данных (например, заменить виртуальную таблицу на обычную).
  • 🛠 Применять постобработку результатов в событии ПриПолученииДанныхНаСервере.

Пример модификации запроса с виртуальной таблицей:

Процедура ДинамическийСписокПередЗапросомНаСервере(Элемент, Запрос, ПараметрыЗапроса)

// Добавляем отбор по дате начала периода

Если НЕ ЗначениеЗаполнено(ПараметрыЗапроса.ДатаНачала) Тогда

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

Иначе

ДатаНачала = НачалоДня(ПараметрыЗапроса.ДатаНачала);

КонецЕсли;

// Виртуальная таблица РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты

// не поддерживает произвольные условия, поэтому модифицируем параметры

Запрос.Текст = СтрЗаменить(Запрос.Текст,

"ПЕРИОД С НачалоДня(&ДатаНачала) ПО КонецДня(&ДатаОкончания)",

"ПЕРИОД С НачалоДня(&НоваяДатаНачала) ПО КонецДня(&ДатаОкончания)");

Запрос.УстановитьПараметр("НоваяДатаНачала", ДатаНачала);

КонецПроцедуры

Если требуется добавить условие, которое не поддерживается виртуальной таблицей, можно пойти на хитрость: использовать временную таблицу для предварительной фильтрации данных, а затем присоединять её к основному запросу. Однако это усложняет логику и может повлиять на производительность.

Что делать, если виртуальная таблица не поддерживает нужное условие?

В таких случаях рекомендуется:

1. Заменить виртуальную таблицу на обычный запрос к регистру с явным указанием полей.

2. Использовать временную таблицу для предварительной выборки данных с нужными условиями, а затем присоединять её к основному запросу.

3. Перенести фильтрацию на этап постобработки (в событии ПриПолученииДанныхНаСервере), но это может снизить производительность при большом объёме данных.

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

Временные таблицы — это мощный инструмент для оптимизации и модификации запросов динамических списков. Они позволяют:

  • 📊 Предварительно отфильтровать данные по сложным условиям.
  • 📊 Объединить данные из нескольких источников.
  • 📊 Упростить основной запрос, перенеся часть логики во временные таблицы.

Пример использования временной таблицы для добавления дополнительного отбора:

Процедура ДинамическийСписокПередЗапросомНаСервере(Элемент, Запрос, ПараметрыЗапроса)

// Создаём временную таблицу с отфильтрованными данными

ТекстЗапроса = "

|ВЫБРАТЬ

| ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,

| ТоварыНаСкладахОстатки.КоличествоОстаток КАК Количество

|ИЗ

| РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаАктивизации, ) КАК ТоварыНаСкладахОстатки

|ГДЕ

| ТоварыНаСкладахОстатки.Номенклатура.Поставщик = &Поставщик";

ЗапросВременнойТаблицы = Новый Запрос(ТекстЗапроса);

ЗапросВременнойТаблицы.УстановитьПараметр("ДатаАктивизации", ТекущаяДата());

ЗапросВременнойТаблицы.УстановитьПараметр("Поставщик", ПараметрыЗапроса.Поставщик);

// Выполняем запрос и сохраняем результат во временную таблицу

Результат = ЗапросВременнойТаблицы.Выполнить();

ВременнаяТаблица = Результат.Выгрузить();

ВременнаяТаблица.Колонки.Добавить("ПометкаУдаления", Новый ОписаниеТипов("Булево"));

// Модифицируем основной запрос, чтобы он использовал временную таблицу

Запрос.Текст = "

|ВЫБРАТЬ

| ВТ.Номенклатура КАК Номенклатура,

| ВТ.Количество КАК Количество

|ИЗ

| &ВременнаяТаблица КАК ВТ

|ГДЕ

| ВТ.ПометкаУдаления = ЛОЖЬ";

Запрос.УстановитьПараметр("ВременнаяТаблица", ВременнаяТаблица);

КонецПроцедуры

Использование временных таблиц особенно полезно, когда:

  • 🔸 Нужно применить сложную логику фильтрации, которую нельзя выразить в одном запросе.
  • 🔸 Требуется объединить данные из разных источников (например, справочников и регистров).
  • 🔸 Необходимо оптимизировать производительность, разделив один сложный запрос на несколько простых.

Однако у этого подхода есть и минусы: увеличение нагрузки на сервер и усложнение поддержки кода. Поэтому временные таблицы стоит использовать только в тех случаях, когда другие методы не подходят.

💡

Если временная таблица используется часто, рассмотрите возможность создания регистра сведений для хранения промежуточных данных. Это снизит нагрузку на сервер при повторных запросах.

5. Типичные ошибки и как их избежать

При модификации запросов динамических списков разработчики часто сталкиваются с одними и теми же ошибками. Вот наиболее распространённые из них и способы их решения:

Ошибка Причина Решение
Неопределённый параметр в запросе Параметр указан в тексте запроса, но не установлен через УстановитьПараметр() Проверьте все параметры в тексте запроса и убедитесь, что они установлены
Синтаксическая ошибка в запросе Неправильно расставлены запятые, скобки или ключевые слова Используйте ПроверкаЗапроса() для валидации текста запроса перед выполнением
Запрос выполняется слишком долго Слишком сложные условия или отсутствие индексов Оптимизируйте запрос: разбейте на подзапросы, используйте временные таблицы или индексы
Данные не обновляются после изменения запроса Кэширование данных на клиенте или сервере Очистите кэш динамического списка (Элемент.ОчиститьКэш()) или отключите кэширование

Одна из самых коварных ошибок — несоответствие структуры запроса и источника данных. Например, если в запросе указаны поля, которых нет в источнике, или наоборот, это может привести к пустому результату или ошибке выполнения. Всегда сверяйте структуру запроса с метаданными источника.

Ещё одна частая проблема — некорректная работа с датами. Например, если в запросе используется параметр даты, но он не инициализирован или имеет неверный формат, это может привести к неожиданным результатам. Всегда проверяйте значения параметров перед их использованием в запросе.

💡

Перед модификацией запроса всегда тестируйте его в консоли запросов (меню Файл → Новый → Запрос). Это поможет выявить синтаксические ошибки и оценить производительность.

6. Оптимизация производительности модифицированных запросов

Изменение запроса динамического списка может существенно повлиять на его производительность. Вот несколько советов, как избежать замедления:

  • Минимизируйте количество полей в запросе. Выбирайте только те, которые действительно нужны для отображения.
  • Используйте индексы. Убедитесь, что поля, по которым идёт фильтрация или сортировка, проиндексированы в базе данных.
  • Избегайте подзапросов в секции ВЫБРАТЬ. Они могут значительно замедлить выполнение.
  • Ограничивайте объём данных. Если возможно, добавляйте условия по дате или другим критериям, чтобы сократить выборку.

Пример оптимизированного запроса:

Процедура ДинамическийСписокПередЗапросомНаСервере(Элемент, Запрос, ПараметрыЗапроса)

// Ограничиваем выборку только нужными полями

Запрос.Текст = СтрЗаменить(Запрос.Текст,

"ВЫБРАТЬ",

"ВЫБРАТЬ ПЕРВЫЕ 1000

| Ссылка.Дата КАК Дата,

| Ссылка.Номер КАК Номер,

| Ссылка.СуммаДокумента КАК Сумма");

// Добавляем условие по дате для ограничения выборки

Если НЕ СтрНайти(Запрос.Текст, "ГДЕ") = 0 Тогда

Запрос.Текст = СтрЗаменить(Запрос.Текст,

"ГДЕ",

"ГДЕ Ссылка.Дата >= &ДатаНачала И");

Запрос.УстановитьПараметр("ДатаНачала", НачалоМесяца(ТекущаяДата()));

Иначе

Запрос.Текст = Запрос.Текст + "

|ГДЕ

| Ссылка.Дата >= &ДатаНачала";

Запрос.УстановитьПараметр("ДатаНачала", НачалоМесяца(ТекущаяДата()));

КонецЕсли;

КонецПроцедуры

Если запрос всё равно выполняется медленно, рассмотрите возможность:

  • 🔄 Разбиения запроса на несколько более простых.
  • 🔄 Использования материализованных представлений (если работает СУБД PostgreSQL).
  • 🔄 Переноса части логики на клиент (например, фильтрация уже полученных данных).

Помните, что динамические списки с модифицированными запросами могут потреблять значительно больше ресурсов сервера, особенно при большом количестве пользователей. Всегда тестируйте изменения на производительность в условиях, близких к боевым.

💡

Для анализа производительности запросов используйте План выполнения запроса (меню Администрирование → Тестирование и исправление → Планы запросов). Это поможет выявить узкие места.

7. Альтернативные подходы: когда модификация запроса не лучшее решение

Иногда модификация запроса динамического списка — не самый эффективный способ достичь цели. Рассмотрите альтернативные подходы:

  • 🔄 Использование отборов. Если нужно просто фильтровать данные, возможно, достаточно настроить отборы в свойствах динамического списка.
  • 🔄 Замена динамического списка на таблицу значений. Если данные нужно сильно трансформировать, возможно, лучше получить их через отдельный запрос и отобразить в таблице значений.
  • 🔄 Использование расширений конфигурации. Если модификации нужны только для конкретного клиента, их можно вынести в расширение, не изменяя основную конфигурацию.
  • 🔄 Создание отдельного отчёта. Если требуется сложная аналитика, возможно, лучше разработать отдельный отчёт вместо модификации динамического списка.

Пример, когда лучше использовать отчёт вместо модификации динамического списка:

  • 📉 Нужно показать данные в нестандартном виде (например, сводная таблица).
  • 📉 Требуется сложная группировка или агрегация.
  • 📉 Данные нужно экспортировать в Excel или другой формат.

Если вы всё же решили модифицировать запрос динамического списка, убедитесь, что это оправдано с точки зрения архитектуры решения. Иногда проще и надёжнее использовать стандартные механизмы платформы.

FAQ: Частые вопросы по модификации запросов динамических списков

Можно ли изменить запрос динамического списка на клиенте?

Нет, модификация запроса возможна только на сервере в соответствующих обработчиках (ПередЗапросомНаСервере, ПриСозданииНаСервере). Попытка изменить запрос на клиенте приведёт к ошибке, так как запрос выполняется на сервере.

Как проверить, что запрос изменился корректно?

Можно вывести текст запроса в отладочное сообщение:

Сообщить(Запрос.Текст);

Или использовать точку останова в отладчике и просмотреть свойства объекта Запрос.

Почему после изменения запроса данные не обновляются?

Возможные причины:

  • Кэш динамического списка не очищен (используйте Элемент.ОчиститьКэш()).
  • Параметры запроса не обновлены.
  • В запросе остались старые условия, которые фильтруют все данные.

Можно ли в динамическом списке использовать объединение таблиц (UNION)?

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

Как модифицировать запрос, если источник данных — это объектный список (например, список документов)?

В этом случае запрос формируется автоматически на основе структуры объекта. Чтобы его изменить, нужно:

  1. Отключить автоматическое формирование запроса (Элемент.АвтоЗапрос = Ложь).
  2. Задать свой текст запроса в обработчике ПриСозданииНаСервере.

Пример:

Процедура ДинамическийСписокПриСозданииНаСервере(Элемент)

Элемент.АвтоЗапрос = Ложь;

Элемент.Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1000 Документ.Ссылка КАК Ссылка ИЗ Документ.ЗаказПокупателя КАК Документ";

КонецПроцедуры