Как ускорить и оптимизировать перенос табелей учета рабочего времени из УПП 1.3 в ЗУП 3.1

Программист 1С v8.3 (Обычные формы) 1С:Зарплата и Управление Персоналом IT и автоматизация бизнеса
← На главную

Переход с конфигурации «Управление производственным предприятием» (УПП 1.3) на «Зарплата и управление персоналом» (ЗУП 3.1) часто сопровождается переносом накопленных исторических данных. Одной из наиболее тяжелых операций становится перенос документов Табель учета рабочего времени. В УПП 1.3 структура табличной части документа состоит из 248 горизонтальных колонок на каждого сотрудника (31 день × 4 вида времени и часов). В ЗУП 3.1 структура табличной части вертикальная: каждая запись по виду времени превращается в отдельную строку.

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

Выясним причины низкой производительности

Проанализируем стандартные ошибки, которые допускаются при написании правил в конфигурации «Конвертация данных 2.1»:

  1. Запрос внутри цикла: Попытка выгрузить документы шапками, а затем для каждого документа выполнять отдельный запрос к табличной части ТабельУчетаРабочегоВремениОрганизацииОтработанноеВремя. Это генерирует тысячи отдельных обращений к СУБД.
  2. Постоянное пересоздание и копирование таблиц значений: Вызов методов Новый ТаблицаЗначений и Скопировать() на каждом шаге цикла приводит к фрагментации оперативной памяти и интенсивной работе сборщика мусора 1С.
  3. Разворачивание колонок на стороне языка 1С: Перебор сотен ячеек и ручное присвоение значений на интерпретируемом языке 1С выполняется значительно медленнее, чем аналогичные преобразования на стороне базы данных.
  4. Избыточное сопоставление НСИ: При каждом вызове ВыгрузитьПоПравилу движок Конвертации данных выполняет поиск и проверку ссылок на физические лица, сотрудники и виды использования рабочего времени.

Рассмотрим пример исходного медленного кода с запросом в цикле:


Для каждого докТабеля Из ТЗ Цикл
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТабельОтработанноеВремя.Сотрудник,
    |	ТабельОтработанноеВремя.ПервыйВидВремени1,
    |	ТабельОтработанноеВремя.ПервыйЧасов1
    |ИЗ
    |	Документ.ТабельУчетаРабочегоВремениОрганизации.ОтработанноеВремя КАК ТабельОтработанноеВремя
    |ГДЕ
    |	ТабельОтработанноеВремя.Ссылка = &Ссылка";
    
    Запрос.УстановитьПараметр("Ссылка", докТабеля.Ссылка);
    ВыборкаВремени = Запрос.Выполнить().Выбрать();
    
    Пока ВыборкаВремени.Следующий() Цикл
        // Добавление строк и ручной перебор 248 колонок
    КонецЦикла;
    
    ВыгрузитьПоПравилу(докТабеля, , , , "ТабельУчетаРабочегоВремени");
КонецЦикла;

Способ 1. Использование ПКО «Документ-в-Документ» и выборка через ПКГС

Чтобы ускорить процесс, перенесем логику с ПВД на правило конвертации объектов (ПКО). Создадим ПКО для документа, а для табличной части настроим правило конвертации группы свойств (ПКГС). В обработчике ПКГС ПередОбработкой сформируем коллекцию объектов единым запросом через КоллекцияОбъектов.

Разберем по шагам, как трансформировать горизонтальную структуру УПП в вертикальную структуру ЗУП прямо в тексте запроса с помощью секций ОБЪЕДИНИТЬ ВСЕ. Это позволит переложить всю тяжелую работу по разворачиванию 248 колонок на СУБД:


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|	ТЧ.Ссылка КАК Ссылка,
|	ТЧ.Сотрудник КАК Сотрудник,
|	ТЧ.ПервыйВидВремени1 КАК ВидВремени,
|	ТЧ.ПервыйЧасов1 КАК Часов,
|	1 КАК День
|ИЗ
|	Документ.ТабельУчетаРабочегоВремениОрганизации.ОтработанноеВремя КАК ТЧ
|ГДЕ
|	ТЧ.Ссылка = &Источник
|	И ТЧ.ПервыйВидВремени1 НЕ ЗНАЧЕНИЕ(Справочник.КлассификаторИспользованияРабочегоВремени.ПустаяСсылка)
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
|	ТЧ.Ссылка,
|	ТЧ.Сотрудник,
|	ТЧ.ВторойВидВремени1,
|	ТЧ.ВторойЧасов1,
|	1
|ИЗ
|	Документ.ТабельУчетаРабочегоВремениОрганизации.ОтработанноеВремя КАК ТЧ
|ГДЕ
|	ТЧ.Ссылка = &Источник
|	И ТЧ.ВторойВидВремени1 НЕ ЗНАЧЕНИЕ(Справочник.КлассификаторИспользованияРабочегоВремени.ПустаяСсылка)";

Запрос.УстановитьПараметр("Источник", Источник);
КоллекцияОбъектов = Запрос.Выполнить().Выгрузить();

Преимущества подхода:

  1. Мы отсекаем незаполненные виды времени сразу в условии ГДЕ запроса, что сокращает передаваемый объем данных в 3–4 раза.
  2. Обработка происходит на стороне SQL-сервера, что на порядки быстрее обхода колонок в цикле 1С.

Способ 2. Пакетная выгрузка данных (порциями)

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

В ПВД формируем список ссылок на документы и разбиваем их на порции, передавая массив ссылок в параметр запроса:


ЗапросДокументы = Новый Запрос;
ЗапросДокументы.Текст = 
"ВЫБРАТЬ
|	Табель.Ссылка КАК Ссылка
|ИЗ
|	Документ.ТабельУчетаРабочегоВремениОрганизации КАК Табель
|ГДЕ
|	Табель.Проведен
|	И Табель.Дата МЕЖДУ &ДатаНач И &ДатаКон";

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

ВыборкаДокументов = ЗапросДокументы.Выполнить().Выбрать();

МассивПорции = Новый Массив;
Счетчик = 0;

Пока ВыборкаДокументов.Следующий() Цикл
    МассивПорции.Добавить(ВыборкаДокументов.Ссылка);
    Счетчик = Счетчик + 1;
    
    Если Счетчик >= 100 Тогда
        ВыгрузитьПакетТабелей(МассивПорции);
        МассивПорции.Очистить();
        Счетчик = 0;
    КонецЕсли;
КонецЦикла;

Если МассивПорции.Количество() > 0 Тогда
    ВыгрузитьПакетТабелей(МассивПорции);
КонецЕсли;

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

Способ 3. Прямая выгрузка через ЗаписьXML без использования КД 2.1

Для максимально быстрой выгрузки больших массивов данных можно полностью отказаться от движка Конвертации данных и сформировать XML-файл вручную с помощью объекта ЗаписьXML.

Рассмотрим алгоритм действий:

  1. В источнике (УПП 1.3) формируем итоговый XML-файл прямой записью через ЗаписьXML, выгружая только необходимые реквизиты (GUID объектов, даты, сотрудников, виды времени и часы).
  2. В приемнике (ЗУП 3.1) с помощью объекта ЧтениеXML считываем файл и создаем документы программно через Документы.Табель.СоздатьДокумент().

Пример формирования файла на стороне источнике:


ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл("C:\Export\tabels.xml");
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьXML.ЗаписатьНачалоЭлемента("ДанныеТабелей");

// Обход выборки и запись структур
ЗаписьXML.ЗаписатьНачалоЭлемента("Документ");
ЗаписьXML.ЗаписатьАтрибут("GUID", Строка(СсылкаНаДок.УникальныйИдентификатор()));
ЗаписьXML.ЗаписатьАтрибут("Дата", Формат(СсылкаНаДок.Дата, "ДФ=ггггММддHHmmss"));

// Запись строк времени
ЗаписьXML.ЗаписатьНачалоЭлемента("СтрокаВремени");
ЗаписьXML.ЗаписатьАтрибут("СотрудникGUID", Строка(СотрудникGUID));
ЗаписьXML.ЗаписатьАтрибут("Часы", Строка(Часов));
ЗаписьXML.ЗаписатьКонецЭлемента(); // СтрокаВремени

ЗаписьXML.ЗаписатьКонецЭлемента(); // Документ
ЗаписьXML.ЗаписатьКонецЭлемента(); // ДанныеТабелей
ЗаписьXML.Закрыть();

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

← На главную