В процессе работы с отраслевым решением «1С:Управление сервисным центром» (УСЦ) пользователи часто сталкиваются с критической проблемой: при попытке открыть обработку «Распределение загрузки мастеров» (раздел «Ремонт и обслуживание» -> «Планирование загрузки») сеанс работы системы полностью зависает. Единственным выходом остается принудительное завершение процесса 1cv8.exe через диспетчер задач. Это парализует работу диспетчеров и мастеров сервисного центра.
В этой статье мы подробно разберем, почему возникает данная проблема, проанализируем узкие места в коде и запросах конфигурации, а также рассмотрим пошаговые методы оптимизации, которые позволят вернуть системе былую производительность.
---
Если запустить отладку и попытаться пошагово пройти процесс открытия формы, мы обнаружим, что зависание происходит в процедуре ЗаполнитьДеревоНеНазначенныхДокументов(). При этом платформа ведет себя крайне странно на первый взгляд:
ПоВыездПрием.Следующий().лСписокДокументов = РеквизитФормыВЗначение("НовыйСписокДокументов") и лСписокДокументов.Строки.Очистить(), могут выполняться по 10–15 минут или вызывать полное зависание потока.Давайте выясним истинные причины такого поведения системы, разделив их на три ключевые технические проблемы.
---
Основной источник беды кроется в тексте запроса, который собирает данные для дерева неназначенных документов. Проанализируем первую временную таблицу запроса — ТекущийЭтапПриемаВыезда:
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ЗаданиеНаРаботу.Работы КАК ЗаданиеНаРаботуРаботы
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ЗаданиеНаРаботу.Работы КАК ЗаданиеНаРаботуРаботыДлительностьЭтапа
ПО ЗаданиеНаРаботуРаботы.Ссылка = ЗаданиеНаРаботуРаботыДлительностьЭтапа.Ссылка
Здесь допущена грубейшая ошибка проектирования структуры БД. Табличная часть Работы документа ЗаданиеНаРаботу соединяется сама с собой только по полю Ссылка. При этом связи по номеру строки или уникальному идентификатору строки (этапа) нет.
Что это означает на практике?
Это классическое декартово произведение (Cartesian Product) в рамках каждого конкретного документа. Если в табличной части «Работы» документа содержится 5 строк, то в результате такого соединения СУБД сгенерирует 25 строк ($5 \times 5$). Если в документе 20 строк — мы получим уже 400 строк. При наличии сотен и тысяч документов в базе данных итоговая выборка раздувается до миллионов дублирующихся записей.
Именно поэтому ограничение выборки с помощью конструкции ПЕРВЫЕ 100 не помогает: СУБД тратит колоссальные ресурсы и дисковое пространство (tempdb в MS SQL) на перемножение таблиц во временной памяти еще до того, как применит финальное ограничение.
---
Теперь разберем, почему зависает строка лСписокДокументов = РеквизитФормыВЗначение("НовыйСписокДокументов").
Метод РеквизитФормыВЗначение производит полную сериализацию и конвертацию данных из структуры управляемой формы в объектные типы данных платформы (в данном случае — в ДеревоЗначений). Если дерево формы ранее уже было заполнено тысячами элементов из-за некорректного запроса, платформа начинает монопольно пересчитывать индексы, связи и элементы управления формы на сервере. Это вызывает резкий скачок потребления оперативной памяти процессом rphost и визуальное зависание.
Аналогично ведет себя и метод ПоВыездПрием.Следующий(). При использовании обхода ОбходРезультатаЗапроса.ПоГруппировкам платформа 1С строит иерархическое дерево в оперативной памяти сервера приложений. Миллионы дублирующихся строк из декартова произведения мгновенно перегружают стек вызовов платформы.
---
В тексте запроса активно используются левые соединения (LEFT JOIN) с документами СЦентр_ОтчетПоРаботеУКлиента, СЦентр_ВозвратИзРемонта и ЗаказПокупателя по реквизиту ДокументОснование (или СЦентр_ДокументОснование).
Если для этих реквизитов в свойствах метаданных конфигурации не установлено свойство «Индексировать», СУБД не может выполнить быстрый поиск по индексу (Index Seek) и переходит к полному сканированию таблиц (Table Scan). На реальных базах объемом более 5–10 Гб это приводит к катастрофическому падению скорости выполнения любых запросов.
---
Для восстановления работоспособности обработки «Распределение загрузки мастеров» выполним три последовательных шага.
Прежде всего, нам необходимо избавиться от дублирующего соединения табличной части Работы саму на себя. Длительность работ можно рассчитать в отдельном пакете запроса, предварительно сгруппировав данные, либо связать таблицы по уникальным полям.
Рассмотрим более производительный вариант получения длительности этапа без использования избыточных соединений:
// Вместо двойного соединения ТЧ "Работы" выберем данные один раз и сгруппируем их
ВЫБРАТЬ
ВыездПрием.Ссылка КАК ДокументПриемВыезд,
ЗаданиеНаРаботуРаботы.Ссылка.ВидЭтапа КАК ТекВидЭтапа,
СУММА(ВЫРАЗИТЬ(РАЗНОСТЬДАТ(ЗаданиеНаРаботуРаботы.ДатаНачала, ЗаданиеНаРаботуРаботы.ДатаОкончания, МИНУТА) / 60 КАК ЧИСЛО(15, 2))) КАК ДлительностьЧасовТекущегоЭтапа
ПОМЕСТИТЬ ВременнаяТаблицаДлительности
ИЗ
ЖурналДокументов.СЦентр_ДокументыПоРемонтуИОбслуживанию КАК ВыездПрием
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ЗаданиеНаРаботу.Работы КАК ЗаданиеНаРаботуРаботы
ПО ВыездПрием.Ссылка = ЗаданиеНаРаботуРаботы.Ссылка.ДокументОснование
ГДЕ
ЗаданиеНаРаботуРаботы.Ссылка.Состояние = ЗНАЧЕНИЕ(Справочник.СостоянияСобытий.Запланировано)
СГРУППИРОВАТЬ ПО
ВыездПрием.Ссылка,
ЗаданиеНаРаботуРаботы.Ссылка.ВидЭтапа
Замените проблемный участок в вашей обработке аналогичным пакетом. Это исключит умножение строк и снизит нагрузку на СУБД в сотни раз.
Для того чтобы избежать долгой конвертации данных формы через РеквизитФормыВЗначение, применим правильный паттерн работы с коллекциями управляемых форм в 1С:
ЗначениеВДанныеФормы.Посмотрим на пример правильной реализации очистки и заполнения:
// Избегаем тяжелого вызова лСписокДокументов = РеквизитФормыВЗначение(...)
// Очищаем элементы дерева непосредственно на форме:
НовыйСписокДокументов.ПолучитьЭлементы().Очистить();
// Получаем результат запроса в плоскую таблицу или дерево на сервере
ТаблицаРезультата = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
// Быстро и безопасно переносим данные в реквизит формы
ЗначениеВДанныеФормы(ТаблицаРезультата, НовыйСписокДокументов);
Такой подход экономит оперативную память сервера и выполняется за доли секунды, так как платформа оптимизирует передачу данных между сервером и клиентом на уровне внутреннего бинарного протокола.
Откройте конфигуратор и найдите документы, которые участвуют в левых соединениях запроса (СЦентр_ОтчетПоРаботеУКлиента, СЦентр_ВозвратИзРемонта, ЗаказПокупателя).
Для реквизита ДокументОснование (или его отраслевого аналога) в свойствах установите значение индексирования:
После внесения изменений в структуру метаданных обязательно выполните обслуживание базы данных. Для этого в режиме «Конфигуратор» перейдите в меню «Администрирование» -> «Тестирование и исправление», отметьте пункты «Реиндексация таблиц» и «Обновление статистики» и запустите процесс.
---
Причиной «мертвого» зависания обработки распределения загрузки мастеров в УСЦ является совокупность ошибок в коде:
JOIN табличной части самой с собой.РеквизитФормыВЗначение на больших объемах избыточных данных.Применив предложенные шаги по исправлению запроса, оптимизации работы с коллекциями формы и индексации СУБД, вы добьетесь мгновенного открытия формы распределения загрузки даже на сильно нагруженных информационных базах.