При разработке печатных форм (реестров, ведомостей, списков сотрудников) часто возникает задача вывести данные табличной части не в том порядке, в котором их ввел пользователь, а в строго алфавитном. На первый взгляд задача кажется простой, однако при её реализации начинающие разработчики часто совершают архитектурные ошибки, которые приводят либо к неверному выводу данных, либо к существенному замедлению работы системы.
В этой статье мы подробно разберем несколько способов решения этой задачи, проанализируем типичные ошибки при написании запросов и рассмотрим стандарты разработки 1С для сортировки данных в оперативной памяти.
Рассмотрим исходный код, с которого часто начинают разработку печатной формы:
ОбластьСтроки = МакетОбработки.ПолучитьОбласть("Строка");
Для каждого ТекущаяСтрока из СсылкаНаДокумент.ФизическиеЛица Цикл
ОбластьСтроки.Параметры.ФизическоеЛицо = ТекущаяСтрока.ФизическоеЛицо;
ТабличныйДокумент.Вывести(ОбластьСтроки);
КонецЦикла;
Этот код просто обходит табличную часть ФизическиеЛица документа СсылкаНаДокумент в том порядке, в котором строки записаны в базе данных (или в котором их ввел пользователь). Напрямую отсортировать коллекцию строк табличной части объекта без изменения самого документа невозможно. Поэтому для решения задачи нам необходимо использовать вспомогательные инструменты: запросы или сортировку во временных таблицах значений.
Самый простой и логичный способ упорядочить данные — использовать объект Запрос. Мы выбираем данные из табличной части документа и накладываем условие упорядочивания по наименованию физического лица.
Однако здесь начинающих разработчиков подстерегает серьезная ошибка. Если написать запрос напрямую к табличной части документа без указания конкретной ссылки, система выберет данные всех документов этого вида, существующих в информационной базе. Выясним причину этой ошибки на примере кода:
// ОШИБОЧНЫЙ КОД (выберет физлиц из ВСЕХ документов базы)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| НачислениеЗарплатыФизическиеЛица.ФизическоеЛицо.Наименование КАК ФизическоеЛицоНаименование
|ИЗ
| Документ.НачислениеЗарплаты.ФизическиеЛица КАК НачислениеЗарплатыФизическиеЛица
|УПОРЯДОЧИТЬ ПО
| ФизическоеЛицоНаименование";
Чтобы запрос возвращал данные только нашего документа, необходимо обязательно добавить условие фильтрации по ссылке. Исправим код и напишем правильный вариант с использованием параметров запроса:
ОбластьСтроки = МакетОбработки.ПолучитьОбласть("Строка");
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТЧ_Документа.ФизическоеЛицо КАК ФизическоеЛицо,
| ТЧ_Документа.ФизическоеЛицо.Наименование КАК ФизическоеЛицоНаименование
|ИЗ
| Документ.НачислениеЗарплаты.ФизическиеЛица КАК ТЧ_Документа
|ГДЕ
| ТЧ_Документа.Ссылка = &СсылкаНаДокумент
|УПОРЯДОЧИТЬ ПО
| ФизическоеЛицоНаименование";
Запрос.УстановитьПараметр("СсылкаНаДокумент", СсылкаНаДокумент);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Заполняем параметр области макета ссылкой на ФизическоеЛицо
ОбластьСтроки.Параметры.ФизическоеЛицо = ВыборкаДетальныеЗаписи.ФизическоеЛицо;
ТабличныйДокумент.Вывести(ОбластьСтроки);
КонецЦикла;
Важное примечание: Обратите внимание, что в запросе мы выбираем как само ФизическоеЛицо (ссылку), так и его строковое представление ФизическоеЛицо.Наименование для сортировки. В параметры макета мы передаем именно ссылку, чтобы в печатной форме работала расшифровка (возможность кликнуть по ФИО и открыть карточку сотрудника).
Описанный выше классический метод с запросом имеет один важный архитектурный недостаток. Запрос всегда выполняется к данным, которые уже записаны в базу данных СУБД.
Представим стандартный сценарий работы пользователя: он открыл существующий документ (или создал новый), изменил состав сотрудников в табличной части, но еще не нажал кнопку «Записать» или «Провести». Если в этот момент он нажмет кнопку «Печать», то прямой запрос к табличной части выведет на печать старые данные из базы данных, проигнорировав изменения на экране. Для нового незаписанного документа печатная форма вообще окажется пустой.
Чтобы печатная форма всегда отражала актуальные данные из оперативной памяти (даже если документ еще не сохранен), нам необходимо получать данные непосредственно из объекта в памяти компьютера.
Для того чтобы объединить преимущества удобной сортировки в запросе и работу с актуальными незаписанными данными из памяти, мы можем выгрузить табличную часть документа в ТаблицаЗначений, передать ее в запрос в качестве параметра и упорядочить уже внутри запроса с помощью временной таблицы.
Рассмотрим этот эффективный и безопасный алгоритм:
// 1. Выгружаем данные табличной части из оперативной памяти
ТаблицаСотрудников = СсылкаНаДокумент.ФизическиеЛица.Выгрузить();
// 2. Передаем таблицу в запрос для сортировки
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВТ_ТЧ.ФизическоеЛицо КАК ФизическоеЛицо
|ПОМЕСТИТЬ ВТ_Сотрудники
|ИЗ
| &ТаблицаСотрудников КАК ВТ_ТЧ
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_Сотрудники.ФизическоеЛицо КАК ФизическоеЛицо
|ИЗ
| ВТ_Сотрудники КАК ВТ_Сотрудники
|УПОРЯДОЧИТЬ ПО
| ВТ_Сотрудники.ФизическоеЛицо.Наименование";
Запрос.УстановитьПараметр("ТаблицаСотрудников", ТаблицаСотрудников);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
// 3. Выводим отсортированные данные в печатную форму
ОбластьСтроки = МакетОбработки.ПолучитьОбласть("Строка");
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
ОбластьСтроки.Параметры.ФизическоеЛицо = ВыборкаДетальныеЗаписи.ФизическоеЛицо;
ТабличныйДокумент.Вывести(ОбластьСтроки);
КонецЦикла;
Этот способ является наиболее универсальным, так как он гарантирует актуальность выводимых данных при любых действиях пользователя и сохраняет высокую производительность за счет сортировки силами СУБД.
Если по каким-то причинам использование запроса нежелательно, мы можем отсортировать данные непосредственно в оперативной памяти, используя метод объекта ТаблицаЗначений.Сортировать(). Однако здесь необходимо строго соблюдать стандарты разработки фирмы «1С».
Как делать НЕЛЬЗЯ: Код вида ТаблицаСотрудников.Сортировать("ФизическоеЛицо Возр") является грубейшей ошибкой производительности. Поскольку колонка ФизическоеЛицо содержит ссылочный тип данных, при выполнении метода Сортировать() платформа 1С будет вынуждена для каждого сравнения строк неявно обращаться к базе данных, чтобы получить текстовое представление (наименование) ссылки. На списке из сотен сотрудников это приведет к лавинообразному росту количества запросов к СУБД (проблема «N+1 запросов») и долгому зависанию программы.
Как делать ПРАВИЛЬНО: Чтобы выполнить требования стандартов разработки и обеспечить мгновенную сортировку, мы должны предварительно создать в таблице значений текстовую колонку, заполнить её строковыми представлениями наименований один раз для каждой строки и выполнить сортировку именно по этой текстовой колонке.
Рассмотрим программную реализацию этого подхода:
// 1. Выгружаем данные табличной части
ТаблицаСотрудников = СсылкаНаДокумент.ФизическиеЛица.Выгрузить();
// 2. Добавляем вспомогательную строковую колонку для ФИО
ТаблицаСотрудников.Колонки.Добавить("ФИО_Строка", Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(150)));
// 3. Заполняем строковое представление один раз для каждой строки (минимизируем обращения к БД)
Для каждого Строка Из ТаблицаСотрудников Цикл
Строка.ФИО_Строка = Строка.ФизическоеЛицо.Наименование;
КонецЦикла;
// 4. Сортируем таблицу в памяти по строковой колонке
ТаблицаСотрудников.Сортировать("ФИО_Строка Возр");
// 5. Выводим результат в табличный документ
ОбластьСтроки = МакетОбработки.ПолучитьОбласть("Строка");
Для каждого Строка Из ТаблицаСотрудников Цикл
ОбластьСтроки.Параметры.ФизическоеЛицо = Строка.ФизическоеЛицо;
ТабличныйДокумент.Вывести(ОбластьСтроки);
КонецЦикла;
Этот метод работает исключительно в оперативной памяти и показывает отличную скорость работы на средних объемах данных, полностью удовлетворяя требованиям оптимизации кода 1С.
При сортировке ссылочных полей в запросах важно помнить о поведении платформы при использовании различных конструкций упорядочивания:
УПОРЯДОЧИТЬ ПО ФизическоеЛицо, система отсортирует записи по их внутреннему уникальному идентификатору (UUID ссылки). На экране это будет выглядеть как абсолютно случайный, хаотичный порядок. Сортировка по ссылочному типу напрямую не дает алфавитного порядка.УПОРЯДОЧИТЬ ПО ФизическоеЛицо.Наименование. Это самый надежный и очевидный способ.АВТОУПОРЯДОЧИВАНИЕ в конце текста запроса заставляет систему автоматически определять основное представление справочника (для физлиц это как раз Наименование) и сортировать по нему. Однако явное указание поля .Наименование считается более предпочтительным стандартом написания кода, так как оно делает логику запроса прозрачной для других разработчиков.Подводя итоги, выделим ключевые правила для вывода отсортированных данных в печатные формы: