Динамический список 1С — это реквизит управляемой формы типа ДинамическийСписок, который показывает строки из базы, не загружая их в форму целиком. На нём построены стандартные формы списков и выбора справочников, документов и регистров. Программист сталкивается с ним, когда в список заказов нужно добавить колонку «Долг», собрать рабочее место менеджера или вывести связанные документы в форме объекта. Ниже — как устроены основная таблица, ключ строки и динамическое считывание и почему именно произвольный запрос чаще всего делает список медленным.
У динамического списка формы нет собственных строк. В реквизите лежат только настройки: источник данных, поля, отбор, порядок, условное оформление. Когда пользователь открывает форму или листает таблицу, сервер строит по ним запрос (внутри работает компоновка данных), выбирает очередную порцию и отдаёт клиенту только видимые строки. Поэтому журнал на миллион документов открывается почти так же быстро, как на сотню, если запрос устроен правильно.
В этом отличие от таблицы значений на форме: ту вы заполняете сами, и все её строки уходят на клиент. Динамический список заполнять не нужно, но и обойти его строки циклом нельзя — на клиенте доступны только текущая и выделенные строки.
В формах списка и выбора, созданных конструктором, реквизит Список уже есть и отмечен как основной. В другую управляемую форму — объекта, общую, форму внешней обработки — список добавляют вручную:
ДинамическийСписок.Список. Колонки добавляются перетаскиванием полей.ПриСозданииНаСервере.Реквизиты и элементы описаны в обзоре управляемой формы, свойства таблицы — в статье о таблице формы.
| Настройка | Что задаёт | На что влияет |
|---|---|---|
ОсновнаяТаблица | Таблицу, строки которой показывает список | Ключ строки, стандартные команды, обновление после записи |
ПроизвольныйЗапрос, ТекстЗапроса | Собственный запрос вместо выборки из одной таблицы | Колонки из других таблиц и нагрузку на СУБД |
| Ключевые поля и вид ключа | Как различать строки без основной таблицы (с 8.3.14) | Чтение порциями и группировку |
ДинамическоеСчитываниеДанных | Читать ли данные порциями по числу видимых строк | Скорость открытия и прокрутки, память сервера |
У справочника или документа ключом строки становится ссылка, у регистра сведений — ключ записи. По ключу платформа понимает, какую форму открыть по Enter, что пометить на удаление, какую строку сделать текущей после обновления. Элементы.Список.ТекущаяСтрока и элементы ВыделенныеСтроки — это ключи.
При произвольном запросе основная таблица должна быть источником, из которого выбирается ссылка, а остальные таблицы подключают левыми соединениями — так, чтобы на одну ссылку приходилось не больше одной строки. Если к заказу присоединились две строки табличной части, ключ задвоится, и список начнёт показывать данные неправильно и терять позицию при прокрутке.
Когда запрос объединяет равноправные источники, например два вида документов, основной таблицы нет. С версии 8.3.14 для него выбирают ключевые поля и вид ключа: «Авто», «Значение поля», «Ключ строки» или «Номер строки». Без них строки просто нумеруются: порций и группировки нет, а добавить объект стандартной командой платформа не может — она не знает, из какой таблицы строки.
Флажок «Динамическое считывание данных» включает чтение порциями: сервер выбирает примерно столько строк, сколько видно в таблице, а при прокрутке — следующие от граничной строки. Чтобы продолжение было однозначным, платформа дописывает к сортировке ключ, например УПОРЯДОЧИТЬ ПО Наименование, Ссылка.
Без флажка при заданной основной таблице сервер читает блоки по 1000 записей в буфер, а без основной таблицы выполняет запрос как есть, и буфер растёт по мере прокрутки — это терпимо только для маленьких выборок. Стандарт 1С «Запросы в динамических списках» рекомендует держать считывание включённым. Оговорка из анонса 8.3.14: для списка с ключевыми полями и сложным запросом к нескольким таблицам его скорее не стоит включать.
Произвольный запрос — самый частый способ добавить колонки и самая частая причина жалоб «список тормозит». Платформа дописывает к вашему тексту отбор, сортировку, ограничение порции и условия доступа и повторяет запрос на каждую порцию. Что замедляет его сильнее всего:
Остатки работает быстро, когда дата не передана, итоги регистра включены, разделение итогов не используется, а внешний запрос задействует все измерения: тогда читается таблица текущих остатков. Если итоги выключены, остатки считаются по движениям. Обороты без периода агрегирует все движения на каждую порцию.СГРУППИРОВАТЬ ПО и РАЗЛИЧНЫЕ. Порцию сгруппированных данных нельзя взять по индексу.Остатки в описанном режиме. Цепочка Контрагент.ГоловнойКонтрагент.ИНН — тоже соединения, а у составного типа — по одному на каждый тип.Дорогую колонку можно заполнить в событии таблицы формы ПриПолученииДанныхНаСервере (с 8.3.10): оно получает только прочитанные строки порции. Такие колонки исключают из отбора, сортировки и группировки методами УстановитьОграниченияИспользованияВОтборе, ...ВПорядке и ...ВГруппировке.
Скорость проверяйте под пользователем с ограниченными правами: при RLS к запросу добавляются условия доступа. Если список уже тормозит, помогут замеры и технологический журнал — см. статью почему 1С тормозит, а диагностику можно заказать.
Задача: в форме списка документа ЗаказПокупателя показать остаток долга по заказу, подсветить должников, запретить сортировку по долгу и посчитать долг по выделенным заказам. Регистр остатков условный: ДолгиПоЗаказам с измерением Заказ и ресурсом Сумма. Настройка реквизита Список: основная таблица Документ.ЗаказПокупателя, включены «Произвольный запрос» и «Динамическое считывание данных», текст запроса:
ВЫБРАТЬ
Заказы.Ссылка КАК Ссылка,
Заказы.ПометкаУдаления КАК ПометкаУдаления,
Заказы.Проведен КАК Проведен,
Заказы.Дата КАК Дата,
Заказы.Номер КАК Номер,
Заказы.Контрагент КАК Контрагент,
Заказы.СуммаДокумента КАК СуммаДокумента,
ЕСТЬNULL(Долги.СуммаОстаток, 0) КАК Долг
ИЗ
Документ.ЗаказПокупателя КАК Заказы
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ДолгиПоЗаказам.Остатки КАК Долги
ПО Заказы.Ссылка = Долги.Заказ
Измерение у регистра одно, поэтому на заказ приходится не больше одной строки, а итоги читаются без группировки. Если в вашем регистре есть ещё, например, Валюта, выводить Долги.Валюта нельзя — заказ с долгом в двух валютах даст две строки с одним ключом. Не упомянуть измерение тоже плохо: суммы валют сложатся. Зафиксируйте его параметром в условии виртуальной таблицы и соединения:
ВЫБРАТЬ
Заказы.Ссылка КАК Ссылка,
Заказы.Дата КАК Дата,
Заказы.Номер КАК Номер,
ЕСТЬNULL(Долги.СуммаОстаток, 0) КАК Долг
ИЗ
Документ.ЗаказПокупателя КАК Заказы
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ДолгиПоЗаказам.Остатки(, Валюта = &Валюта) КАК Долги
ПО Заказы.Ссылка = Долги.Заказ
И (Долги.Валюта = &Валюта)
Параметр задают в ПриСозданииНаСервере через Список.Параметры.УстановитьЗначениеПараметра. Модуль формы списка (команды ОткрытьКонтрагента и ДолгПоВыделенным созданы в редакторе формы):
// Модуль формы списка документа ЗаказПокупателя.
// Реквизит Список: ДинамическийСписок, основная таблица Документ.ЗаказПокупателя,
// произвольный запрос с полями Контрагент и Долг (остаток регистра ДолгиПоЗаказам).
// Команды формы: ОткрытьКонтрагента, ДолгПоВыделенным.
#Область ОбработчикиСобытийФормы
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Контрагент нужен в ТекущиеДанные, даже если колонку скроют.
Список.УстановитьОбязательноеИспользование("Контрагент", Истина);
// Сортировка и группировка по вычисляемой колонке заставят СУБД
// обрабатывать всю выборку, поэтому запрещаем их.
ОграничиваемыеПоля = Новый Массив;
ОграничиваемыеПоля.Добавить("Долг");
Список.УстановитьОграниченияИспользованияВПорядке(ОграничиваемыеПоля);
Список.УстановитьОграниченияИспользованияВГруппировке(ОграничиваемыеПоля);
ВыделитьЗаказыСДолгом();
КонецПроцедуры
#КонецОбласти
#Область ОбработчикиКомандФормы
&НаКлиенте
Процедура ОткрытьКонтрагента(Команда)
ТекущиеДанные = Элементы.Список.ТекущиеДанные;
Если ТекущиеДанные = Неопределено
Или ТипЗнч(Элементы.Список.ТекущаяСтрока) = Тип("СтрокаГруппировкиДинамическогоСписка") Тогда
Возврат;
КонецЕсли;
Если ЗначениеЗаполнено(ТекущиеДанные.Контрагент) Тогда
ПоказатьЗначение(, ТекущиеДанные.Контрагент);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ДолгПоВыделенным(Команда)
Заказы = Новый Массив;
Для Каждого Ключ Из Элементы.Список.ВыделенныеСтроки Цикл
// При группировке строк в выделение попадают и ключи групп.
Если ТипЗнч(Ключ) = Тип("ДокументСсылка.ЗаказПокупателя") Тогда
Заказы.Добавить(Ключ);
КонецЕсли;
КонецЦикла;
Если Заказы.Количество() = 0 Тогда
ПоказатьПредупреждение(, "Выделите заказы в списке.");
Возврат;
КонецЕсли;
Сумма = ДолгПоЗаказам(Заказы);
ПоказатьПредупреждение(, "Долг по выделенным заказам: " + Формат(Сумма, "ЧДЦ=2; ЧН=0"));
КонецПроцедуры
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
&НаСервере
Процедура ВыделитьЗаказыСДолгом()
ЭлементОформления = Список.УсловноеОформление.Элементы.Добавить();
ЭлементОформления.Представление = "Заказы с долгом";
ЭлементОтбора = ЭлементОформления.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Долг");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Больше;
ЭлементОтбора.ПравоеЗначение = 0;
ЭлементОформления.Оформление.УстановитьЗначениеПараметра("ЦветТекста", WebЦвета.Красный);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ДолгПоЗаказам(Знач Заказы)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЕСТЬNULL(СУММА(Долги.СуммаОстаток), 0) КАК Сумма
|ИЗ
| РегистрНакопления.ДолгиПоЗаказам.Остатки(, Заказ В (&Заказы)) КАК Долги";
Запрос.УстановитьПараметр("Заказы", Заказы);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.Сумма;
КонецЕсли;
Возврат 0;
КонецФункции
#КонецОбласти
ЛЕВОЕ СОЕДИНЕНИЕ ... Остатки — виртуальная таблица без даты, при включённых итогах читаются текущие остатки. Соединение левое, чтобы заказы без долга не пропали; ЕСТЬNULL превращает пустой остаток в ноль.УстановитьОбязательноеИспользование("Контрагент", Истина) — поле будет в ТекущиеДанные, даже если колонку скроют: пользователь через «Еще → Изменить форму» или разработчик свойством Видимость. То же делает флажок «Использовать всегда» у поля реквизита.УстановитьОграниченияИспользованияВПорядке и ...ВГруппировке — по «Долгу» нельзя сортировать и группировать ни щелчком по заголовку, ни в настройках. Отбор оставлен, чтобы искать должников, но на большом журнале он медленный.ТипЗнч(Элементы.Список.ТекущаяСтрока) — при включённой группировке текущей может быть строка группы типа СтрокаГруппировкиДинамическогоСписка. По ней, как и по пустому контрагенту, открывать нечего.Список.УсловноеОформление — оформление самого списка. Поле в отборе задаётся именем поля запроса, без префикса Список.ВыделенныеСтроки — массив ключей, при группировке в нём есть и ключи групп, поэтому тип проверяется.Список с основной таблицей обновляется сам, когда объект этой таблицы записывают из формы. Но долг меняет другой документ — оплата. Заказ не записывался, и «Долг» останется прежним. Оповестите списки заказов из формы оплаты:
// Модуль формы документа оплаты (реквизит документа Заказ).
&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
// Сам заказ не записывался, но его долг в списках заказов изменился.
Если ЗначениеЗаполнено(Объект.Заказ) Тогда
ОповеститьОбИзменении(Объект.Заказ);
КонецЕсли;
КонецПроцедуры
Если у списка нет основной таблицы или нужно своё событие, используйте пару Оповестить — ОбработкаОповещения: в форме оплаты вызовите Оповестить("ИзмененаОплатаЗаказа", Объект.Заказ), а в форме со списком обработайте его:
// Модуль формы, на которой размещен динамический список.
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
Если ИмяСобытия = "ИзмененаОплатаЗаказа" Тогда
Элементы.Список.Обновить();
КонецЕсли;
КонецПроцедуры
Изменения фонового задания или другого пользователя список покажет после обновления или по автообновлению с периодом в секундах из настройки списка. При тяжёлом запросе короткий период не ставьте: таймер сработает у каждого, у кого открыта форма.
В управляемой форме 1С список справочника или документа чаще всего живёт в стандартной форме списка или выбора. Параметры Отбор, ТекущаяСтрока, РежимВыбора передаются при открытии — примеры в статье как открыть форму программно. Отбор из кода и параметры запроса разобраны в материале отбор динамического списка программно.
Элементы.Список.ТекущиеДанные.Контрагент. В текущих данных есть только выведенные поля и поля с «Использовать всегда». Колонку могли удалить, скрыть свойством Видимость или через «Изменить форму».ТекущиеДанные = Неопределено: в пустом списке текущей строки нет.ПриПолученииДанныхНаСервере.ОповеститьОбИзменении или Оповестить.Нет. Динамические списки в 1С 8.3 — тип реквизита управляемой формы. В обычных формах ту же роль играет табличное поле с данными типа СправочникСписок или ДокументСписок.
Через Элементы.Список.ТекущаяСтрока: в списке справочника или документа это ссылка, а при группировке — возможно, строка группы. Значения колонок берите из ТекущиеДанные с проверкой на Неопределено, прочие реквизиты — на сервере по ссылке.
Список не следит за базой и перечитывает данные по оповещению, по команде «Обновить», при смене отбора или сортировки и по таймеру автообновления.
Да, на сервере, обычно в ПриСозданииНаСервере: включите ПроизвольныйЗапрос, задайте ТекстЗапроса, ОсновнаяТаблица и ДинамическоеСчитываниеДанных — до обращения к Список.Параметры, иначе источник настроек инициализируется повторно. На БСП стандарт 1С предписывает ОбщегоНазначения.УстановитьСвойстваДинамическогоСписка(Элементы.Список, СвойстваСписка), структуру даёт ОбщегоНазначения.СтруктураСвойствДинамическогоСписка(). Подробнее — в статье динамический список программно.
Если список заказов открывается долго, колонки вроде «Долг» или «Статус оплаты» считаются неверно или нужно рабочее место с несколькими связанными списками, задачу быстрее отдать программисту. Опишите форму и нужные колонки и разместите задачу на 1c-only.ru — оценка бесплатная, ответ в течение рабочего дня. Другие доработки, включая ускорение медленных форм, — на странице услуг.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.