Почему сортировка табличной части и таблиц значений в управляемых формах 1С работает значительно медленнее, чем в обычных?

Программист 1С v8.3 (Управляемые формы)
← На главную

Когда мы переходим от обычных форм к управляемым в 1С:Предприятии 8.3, нередко сталкиваемся с тем, что привычные операции начинают выполняться гораздо медленнее. Одной из таких операций является сортировка табличных данных, будь то ДанныеФормыКоллекция (табличная часть формы) или ТаблицаЗначений, являющаяся реквизитом формы. Пользователи замечают, что даже для относительно небольшого объема данных, например, 1000 строк, сортировка может занимать 2-3 секунды, тогда как в обычных формах она отрабатывала мгновенно. Давайте разберем, почему это происходит и как можно оптимизировать этот процесс.

Понимаем основную причину замедления: клиент-серверное взаимодействие

Главное отличие управляемых форм от обычных заключается в архитектуре клиент-серверного взаимодействия. В обычных формах весь код выполнялся на клиенте (толстом клиенте), и операции с данными, такими как ТаблицаЗначений, происходили локально, без сетевых задержек. В управляемых формах же значительная часть логики выполняется на сервере, а данные передаются между клиентом и сервером. Именно эта передача данных и связанные с ней операции являются основной причиной замедления.

Рассмотрим подробнее, что происходит при вызове метода Сортировать() для ДанныеФормыКоллекция или ТаблицаЗначений, являющейся реквизитом формы:

  1. Неявный серверный вызов: Даже если вы вызываете Сортировать() на клиенте для ДанныеФормыКоллекция, система автоматически выполняет неявный серверный вызов. Это означает, что данные формы (включая вашу табличную часть) должны быть сериализованы на клиенте.
  2. Передача данных на сервер: Сериализованные данные передаются по сети на сервер.
  3. Десериализация данных на сервере: На сервере данные десериализуются обратно в объекты 1С.
  4. Выполнение сортировки на сервере: Сама операция сортировки выполняется на сервере.
  5. Сериализация данных на сервере: Отсортированные данные снова сериализуются на сервере.
  6. Передача данных на клиент: Сериализованные данные передаются обратно по сети на клиент.
  7. Десериализация данных на клиенте: На клиенте данные десериализуются и обновляется отображение табличной части.

Каждый из этих шагов, особенно сериализация/десериализация и сетевая передача, занимает время. Для 1000 строк, этот процесс может легко поглотить 2-3 секунды. Замеры производительности обычно показывают, что основное время тратится именно на серверный вызов и передачу данных, а не на саму сортировку.

Оптимизация клиент-серверного взаимодействия

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

1. Минимизация данных, хранящихся на клиенте

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

Решение: Используйте временное хранилище.

Как это работает:

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

Пример использования временного хранилища:

На сервере при создании или первом заполнении таблицы:


&НаСервере
Процедура ЗаполнитьИСохранитьТЗНаСервере()

    // Создаем и заполняем ТаблицуЗначений
    ПеременнаяТаблицаЗначений = Новый ТаблицаЗначений;
    // ... заполнение таблицы ...

    // Помещаем ТЗ во временное хранилище и сохраняем адрес в реквизите формы
    АдресТЗНаСервере = ПоместитьВоВременноеХранилище(ПеременнаяТаблицаЗначений, УникальныйИдентификатор);
    РеквизитФормыАдресТЗ = АдресТЗНаСервере; // Реквизит формы типа Строка

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

На сервере для работы с таблицей:


&НаСервере
Процедура РаботатьСТЗИзХранилищаНаСервере()

    // Получаем адрес из реквизита формы
    АдресТЗ = РеквизитФормыАдресТЗ;

    // Получаем ТаблицуЗначений из временного хранилища
    ПеременнаяТаблицаЗначений = ПолучитьИзВременногоХранилища(АдресТЗ);

    // ... выполняем операции с ПеременнаяТаблицаЗначений, например, сортировку ...
    ПеременнаяТаблицаЗначений.Сортировать("ИмяКолонки");

    // Если данные изменились и нужно их сохранить в хранилище, обновляем
    АдресТЗНаСервере = ПоместитьВоВременноеХранилище(ПеременнаяТаблицаЗначений, УникальныйИдентификатор);
    РеквизитФормыАдресТЗ = АдресТЗНаСервере;

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

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

2. Использование внеконтекстных серверных вызовов

Если для выполнения серверной процедуры не нужен весь контекст формы (т.е. нет необходимости в автоматической передаче всех реквизитов формы на сервер и обратно), используйте внеконтекстные серверные вызовы с директивой &НаСервереБезКонтекста.

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


&НаСервереБезКонтекста
Функция ОтсортироватьТаблицуНаСервереБезКонтекста(ТаблицаДляСортировки)

    ТаблицаДляСортировки.Сортировать("ИмяКолонки");
    Возврат ТаблицаДляСортировки;

КонецФункции

&НаКлиенте
Процедура ОтсортироватьКлиентскуюТаблицу()

    // Предположим, у нас есть клиентская ТЗ, которую нужно отсортировать
    КлиентскаяТЗ = Новый ТаблицаЗначений;
    // ... заполнение КлиентскаяТЗ ...

    // Передаем только саму ТЗ на сервер
    КлиентскаяТЗ = ОтсортироватьТаблицуНаСервереБезКонтекста(КлиентскаяТЗ);

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

Однако важно понимать, что если ваша ДанныеФормыКоллекция является реквизитом объекта или формы, то для её сортировки всегда потребуется контекст, и внеконтекстный вызов будет неприменим напрямую к ней.

3. Объединение нескольких серверных вызовов в один

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

Особенности сортировки ссылочных типов и метод СравнениеЗначений

Еще одной потенциальной причиной замедления сортировки, особенно по колонкам с ссылочными типами (например, ссылки на справочники, документы), является способ сравнения значений по умолчанию.

По умолчанию, если не указано иное, метод Сортировать() может пытаться сравнивать ссылочные значения по их строковому представлению. Получение строкового представления для каждой ссылки из базы данных – это отдельный запрос, который выполняется в цикле для каждой пары сравниваемых элементов. Для 1000 строк это может означать тысячи запросов к базе данных, что катастрофически замедляет процесс.

Решение: Явно указывать правило сравнения с помощью параметра ОбъектСравнения метода Сортировать(), используя Новый СравнениеЗначений.

Как это работает:

Объект СравнениеЗначений предназначен для сравнения значений по правилам, совпадающим с правилами сравнения значений в базе данных:

Использование Новый СравнениеЗначений гарантирует, что для ссылочных типов сравнение будет происходить по их уникальным идентификаторам, а не по представлениям, что значительно сокращает количество обращений к базе данных.

Пример кода:


&НаКлиенте
Процедура ПоЦенеНажатие(Элемент)

    // Реквизит формы 'ЛучшиеПредложения' типа ДанныеФормыКоллекция
    // Сортировка по нескольким колонкам, с указанием объекта сравнения
    // для оптимизации работы со ссылочными типами
    Объект.ЛучшиеПредложения.Сортировать("СортировочныеЗначения, ИскомыйТовар Убыв, ЦенаПродажи", Новый СравнениеЗначений);

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

Важно отметить, что в рассматриваемом случае автор форума сообщил, что применение Новый СравнениеЗначений не принесло значительной разницы в скорости. Это может означать, что в его конкретной ситуации основной бутылочное горлышко не в получении представлений, а именно в накладных расходах на клиент-серверное взаимодействие и сериализацию/десериализацию данных, даже для простых типов колонок.

Альтернативные подходы к сортировке

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

1. Использование СписокЗначений.СортироватьПоЗначению()

Метод СортироватьПоЗначению() объекта СписокЗначений выполняет сортировку элементов на клиенте и, как правило, не требует серверного вызова. Если данные, которые вы хотите отсортировать, могут быть представлены в виде СписокЗначений (например, если это одномерный список или вам нужно сортировать по одному полю), это может быть очень быстрым решением для клиентской сортировки.

Пример:


&НаКлиенте
Процедура СортироватьСписокЗначенийНаКлиенте()

    МойСписок = Новый СписокЗначений;
    МойСписок.Добавить("Яблоко", 10);
    МойСписок.Добавить("Апельсин", 5);
    МойСписок.Добавить("Банан", 8);

    // Сортировка по значению (т.е. по числу)
    МойСписок.СортироватьПоЗначению(); 

    // Теперь элементы будут отсортированы: Апельсин, Банан, Яблоко

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

2. Сортировка полностью на сервере

Если отсортированные данные не нужны на клиенте для интерактивного отображения (или их можно загрузить на клиент уже отсортированными, но не сортировать интерактивно), выполняйте сортировку исключительно на сервере. Заполняйте ТаблицуЗначений на сервере, сортируйте её там, а затем, если нужно, загружайте в ДанныеФормыКоллекция.

Пример:


&НаСервере
Процедура ЗаполнитьИОтсортироватьТЧНаСервере()

    // Создаем временную ТаблицуЗначений
    ВременнаяТЗ = Новый ТаблицаЗначений;
    ВременнаяТЗ.Колонки.Добавить("Колонка1");
    ВременнаяТЗ.Колонки.Добавить("Колонка2");
    // ... заполнение ВременнаяТЗ ...

    // Сортировка ВременнаяТЗ на сервере
    ВременнаяТЗ.Сортировать("Колонка1 Возр, Колонка2 Убыв");

    // Загружаем отсортированные данные в ТабличнуюЧастьФормы
    Объект.ЛучшиеПредложения.Загрузить(ВременнаяТЗ);

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

Это уменьшает количество сортировок и объем данных, которые туда-сюда "ездят" между клиентом и сервером.

3. Фоновые задания для очень больших объемов

Для крайне больших объемов данных, где сортировка занимает длительное время (десятки секунд или минуты), рассмотрите использование фоновых заданий. Фоновое задание выполняется асинхронно на сервере, не блокируя пользовательский интерфейс. Пользователь может продолжать работать, пока данные сортируются. После завершения фонового задания, результат можно получить и отобразить.

Это не ускоряет саму сортировку, но улучшает пользовательский опыт, так как форма не "зависает".

4. Сортировка динамических списков

Если ваши данные представлены в динамическом списке, то платформа 1С обычно сама эффективно справляется с сортировкой. При нажатии на заголовок колонки динамического списка, сортировка происходит на уровне запроса к базе данных, что для больших объемов данных является наиболее производительным решением. Настроить сортировку в динамическом списке также можно через стандартные средства "Настроить список" на вкладке "Сортировка".

5. Индексирование для ТаблицыЗначений (косвенно)

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


&НаСервере
Процедура СортироватьСИндексами(ТаблицаЗначенийСИсходнымиДанными)

    // Создаем индексы для ускорения сортировки/поиска
    ТаблицаЗначенийСИсходнымиДанными.Индексы.Добавить("КолонкаДляСортировки"); 

    // Теперь можно выполнять сортировку
    ТаблицаЗначенийСИсходнымиДанными.Сортировать("КолонкаДляСортировки");

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

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

Выводы и рекомендации

Мы выяснили, что медленная сортировка в управляемых формах 1С для ДанныеФормыКоллекция и ТаблицаЗначений (реквизита формы) — это ожидаемое поведение, обусловленное архитектурой клиент-серверного взаимодействия и связанными с ним накладными расходами на сериализацию/десериализацию и передачу данных. Несмотря на то, что сама операция сортировки может быть быстрой, она "утопает" в этих системных задержках.

Чтобы минимизировать проблему, рекомендуем:

  1. Анализировать потребность в данных на клиенте: Храните на клиенте только те данные, которые абсолютно необходимы для отображения или интерактивной работы. Объемные вспомогательные таблицы лучше держать во временном хранилище на сервере.
  2. Оптимизировать правила сравнения: Всегда используйте Новый СравнениеЗначений при сортировке по ссылкам, чтобы избежать лишних обращений к базе данных.
  3. Выполнять трудоемкие операции на сервере: Сортировку и другие объемные обработки данных, если это возможно, выполняйте на сервере до загрузки на клиент.
  4. Использовать подходящие инструменты: Для простых одномерных списков рассмотрите СписокЗначений.СортироватьПоЗначению(). Для отображения больших объемов данных используйте динамические списки.
  5. Избегать дублирования данных и сортировок: Как было замечено в теме, повторная сортировка одних и тех же данных сначала в ТаблицаЗначений, а затем в ДанныеФормыКоллекция, только усугубляет проблему. Выполняйте сортировку в одном месте.

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

← На главную