В «1С:ЗУП КОРП 3.1» тормоза (медленная работа) в журналах документов при использовании критериев отбора

04.01.18

База данных - HighLoad оптимизация

Статья для тех, у кого: 1) используете RLS (ограничение доступа на уровне записей и полей), 2) много документов (более 100 тысяч в одном журнале), 3) используете «Журналы документов», 4) отборы в динамическом списке (по сотруднику). Скорее всего, Вы испытали на себе, как отбор происходит минутами (жутко тормозит) – тогда Вам сюда.

Введение

В 1С когда то давно появился новый объект конфигурации «Критерий отбора» зачем он нужен и какой он хороший писали не раз, например, //infostart.ru/public/401743/

 

Вопросы производительно и особенности использования так же осуждали не раз, например, //infostart.ru/public/548778/

 

Но, например, в типовом «1С: Зарплата и Управление Персоналом КОРП, редакция 3.1» и многих других типовых конфигураций используют данный механизм критерия отборов не обращая внимания на вопросы производительности. ПОЧЕМУ?!

 

Ответ, наверное, очень прост: использование «Журналов документов» и «Критериев отбора» упрощает разработку типовых конфигураций, а вопросы производительности пусть потом решают специально обученные люди, как бы 1С намекает зачем нужны «1С:Эксперт по технологическим вопросам» да и вообще Франчайзинг или Консалтинг.

 

Более детально описание проблемы в «Журнале документов», например, «Кадровые документы» при установки отбора по сотруднику происходит зависание и после этого медленно работает пролистывание списка. Смотрим в код и что видим?! При установки/изменения отбора по сотруднику при помощи «Критерия отбора» «Документы сотрудников» происходит получение массива документов, где есть физ.лицо выбранного сотрудника, а затем этот массив устанавливается в отбор динамического списка в «Журнале документов» «Кадровые документы».

 

Вроде все «кашерно» и логично, но есть несколько но:

  1. Основная причина проблем. Список видов документов в «Журнале документов» «Кадровые документы» и «Критерия отбора» «Документы сотрудников» сильно различается. Скриншоты в конце статьи.
  2. Избыточные чтения ненужных таблиц документов при получении массива документов для отбора.
    В результате чего будет произведен поиск при помощи «Критерия отбора» «Документы сотрудников» по видам документов, которые не входят в «Журнале документов» «Кадровые документы».
  3. Избыточные данные в массиве документов для отбора.
    В результате чего в массив документов, который будет получен из «Критерия отбора» «Документы сотрудников» могут входить документы, которых нет в «Журнале документов» «Кадровые документы».
  4. Сам механизм «Критерии отбора» работает не идеально, то есть выполнения запроса ко всем документам из критерия отбора работает быстрее.

При попытке сделать динамический конструктор запроса, который эмулирует работу запроса, по критерию отбора получилась довольно универсальная функция «ПолучитьТекстЗапроса_ДокументыСотрудников()" (пример ниже по тексту).

Далее встал вопрос как «зарулить» все вызовы из журналов документов на нужный мне код, и о СПАСИБО функций которых надо поправить было всего ТРИ и изменения в одну строчку. Надо отдать должное разработчикам типовых конфигураций, которые сделали общие функции для работы отборов по сотрудникам - универсальными функциями и использовали их по максимум.

 

Вся доработка заключалась в том, что бы:

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

 

Итого при использовании журналов документов:

  1. скорость работы выросла примерно в 200 раз,
  2. количество чтений уменьшилось в 300 раз.

Думаю хороший результат.

Проверка и доработка проводилась на «Зарплата и управление персоналом КОРП, редакция 3.1 (3.1.3.224) (http://v8.1c.ru/hrmcorp)».

 

Листинг кода:

ОбщийМодуль.ЗарплатаКадры
Процедура ПараметрКритерияОтбораНаФормеСДинамическимСпискомПриИзменении(Форма, ИмяЭлемента) Экспорт
	
	НайденныеСтроки = Форма.ПараметрыКритерияОтбора.НайтиСтроки(Новый Структура("ИмяЭлементаФормыПараметра", ИмяЭлемента));
	Если НайденныеСтроки.Количество() > 0 Тогда
		// Изменение параметра, установим использование
		СтрокаПараметра = НайденныеСтроки[0];
		Форма[СтрокаПараметра.ИмяРеквизитаФормыПараметраИспользование] = Истина;
	Иначе
		НайденныеСтроки = Форма.ПараметрыКритерияОтбора.НайтиСтроки(Новый Структура("ИмяЭлементаФормыПараметраИспользование", ИмяЭлемента));
		Если НайденныеСтроки.Количество() = 0 Тогда
			Возврат;
		КонецЕсли;
		// Изменение использования параметра
		СтрокаПараметра = НайденныеСтроки[0];
	КонецЕсли;
	
	МассивДокументов = Новый Массив;
	Если Форма[СтрокаПараметра.ИмяРеквизитаФормыПараметраИспользование] Тогда
		// +++ замена критерии отбора на прямые запросы
		//МассивДокументов = ДокументыПоФизическомуЛицу(Форма[СтрокаПараметра.ИмяРеквизитаФормыПараметра]);
		МассивДокументов = ДокументыПоФизическомуЛицу(Форма[СтрокаПараметра.ИмяРеквизитаФормыПараметра], Форма.ИмяФормы);
		// --- 
	КонецЕсли;
	
	Список = Форма[Форма.НаименованиеРеквизитаФормыДинамическийСписок];
	ОбщегоНазначенияКлиентСервер.УстановитьПараметрДинамическогоСписка(
		Список,
		СтрокаПараметра.ИмяПараметра,
		МассивДокументов,
		Форма[СтрокаПараметра.ИмяРеквизитаФормыПараметраИспользование]);
	
КонецПроцедуры

Процедура ОбновитьФормыСДинамическимСписком(Форма) Экспорт
	
	Список = Форма[Форма.НаименованиеРеквизитаФормыДинамическийСписок];
	Для Каждого СтрокаПараметра Из Форма.ПараметрыКритерияОтбора Цикл
		МассивДокументов = Новый Массив;
		Если Форма[СтрокаПараметра.ИмяРеквизитаФормыПараметраИспользование] Тогда
			// +++ замена критерии отбора на прямые запросы
			//МассивДокументов = ДокументыПоФизическомуЛицу(Форма[СтрокаПараметра.ИмяРеквизитаФормыПараметра]);
			МассивДокументов = ДокументыПоФизическомуЛицу(Форма[СтрокаПараметра.ИмяРеквизитаФормыПараметра], Форма.ИмяФормы);
			// --- 
			
			ОбщегоНазначенияКлиентСервер.УстановитьПараметрДинамическогоСписка(
				Список,
				СтрокаПараметра.ИмяПараметра,
				МассивДокументов,
				Форма[СтрокаПараметра.ИмяРеквизитаФормыПараметраИспользование]);
		Иначе
			Для каждого ЭлементФормы Из Форма.Элементы Цикл
				Если ТипЗнч(ЭлементФормы) = Тип("ТаблицаФормы") И ЭлементФормы.ПутьКДанным = Форма.НаименованиеРеквизитаФормыДинамическийСписок Тогда
					ЭлементФормы.Обновить();
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

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


ОбщийМодуль.add_ОбщийМодуль (* компиляция сервер)
Процедура ПереопределениеДокументыПоФизическомуЛицу(ТекстЗапроса, ИмяФормы) Экспорт 
	
	Если СтрНайти(ИмяФормы, "ЖурналДокументов.") > 0 И СтрНайти(ИмяФормы, ".Форма.ФормаСписка") > 0 Тогда 
		Поз = Найти(ИмяФормы, ".Форма.ФормаСписка");
		ИмяЖурнала = Сред(ИмяФормы, 18, Поз - 18);
	Иначе 
		Возврат;
	КонецЕсли;
	
	НовыйТекст = "";
	НовыйТекст = add_ОбщегоНазначенияПовтИсп.ПолучитьТекстЗапроса_ДокументыСотрудников(ИмяЖурнала);

	Если ЗначениеЗаполнено(НовыйТекст) Тогда 
		ТекстЗапроса = НовыйТекст;
	КонецЕсли;
КонецПроцедуры

ОбщийМодуль.add_ОбщегоНазначенияПовтИсп (* компиляция - «Сервер», повторное использование - «На время сеанса»)

Функция ПолучитьТекстЗапроса_ДокументыСотрудников(ИмяЖурналаДокументов) Экспорт 
	Попытка
		_СЗ = Новый СписокЗначений;	

		Для Каждого _Док Из Метаданные.Документы Цикл 
			Если НЕ Метаданные.ЖурналыДокументов[ИмяЖурналаДокументов].РегистрируемыеДокументы.Содержит(_Док) Тогда 
				Продолжить;
			КонецЕсли;
			
			Для Каждого _Рек Из _Док.Реквизиты Цикл 
				Если Метаданные.КритерииОтбора.ДокументыСотрудников.Состав.Содержит(_Рек) Тогда 
					_СЗ.Добавить(_Док.Имя, _Рек.Имя);			
				КонецЕсли;
			КонецЦикла;
			
			Для Каждого _ТЧ Из _Док.ТабличныеЧасти Цикл 
				Для Каждого _Рек Из _ТЧ.Реквизиты Цикл 
					Если Метаданные.КритерииОтбора.ДокументыСотрудников.Состав.Содержит(_Рек) Тогда 
						_СЗ.Добавить(_Док.Имя + "." + _ТЧ.Имя, _Рек.Имя);			
					КонецЕсли;
				КонецЦикла;
			КонецЦикла;
		КонецЦикла;
		
		Шаблон = "Выбрать Ссылка из Документ.#Документ# где #Поле# = &ФизическоеЛицо";
		Разделитель = Символы.ПС + "ОБЪЕДИНИТЬ ВСЕ" + Символы.ПС;
		Текст = "";
		ЭтоПервый = Истина;
		
		Для Каждого _Строка Из _СЗ Цикл 
			Если ЭтоПервый Тогда 
				СтрокаШаблон = СтрЗаменить("Выбрать Разрешенные Ссылка Из Документ.#Документ# Где #Поле# = &ФизическоеЛицо", "#Документ#", _Строка.Значение);
				СтрокаШаблон = СтрЗаменить(СтрокаШаблон, "#Поле#", _Строка.Представление);
				Текст = СтрокаШаблон;	
				ЭтоПервый = Ложь;
			Иначе 
				СтрокаШаблон = СтрЗаменить(Шаблон, "#Документ#", _Строка.Значение);
				СтрокаШаблон = СтрЗаменить(СтрокаШаблон, "#Поле#", _Строка.Представление);
				Текст = Текст + Разделитель + СтрокаШаблон;
			КонецЕсли;
		КонецЦикла;
	Исключение
		Сообщить(ОписаниеОшибки());
		Текст = "";
	КонецПопытки;
		
	Возврат Текст;
КонецФункции

Исключения
ЖурналДокументов.КадровыеДокументы.Форма.ФормаСписка.Форма.Модуль 	

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
	
	// Пропускаем инициализацию, чтобы гарантировать получение формы при передаче параметра "АвтоТест".
	Если Параметры.Свойство("АвтоТест") Тогда
		Возврат;
	КонецЕсли;
	
	Если Параметры.Свойство("Заголовок") Тогда
		Заголовок = Параметры.Заголовок;
	КонецЕсли;
	
	Параметры.Свойство("СотрудникСсылка", СотрудникСсылка);
	
	Если ЗначениеЗаполнено(СотрудникСсылка) Тогда
		
		КадровыеДанныеСотрудников = КадровыйУчет.КадровыеДанныеСотрудников(Истина, СотрудникСсылка, "ГоловнаяОрганизация,ФизическоеЛицо,ТекущаяОрганизация");
		Если КадровыеДанныеСотрудников.Количество() > 0 Тогда
			
			ДанныеСотрудника = КадровыеДанныеСотрудников[0];
			
			Организация = ДанныеСотрудника.ТекущаяОрганизация;
			
			ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(
				Список, "ГоловнаяОрганизация", ДанныеСотрудника.ГоловнаяОрганизация);
				
			// +++ замена критерии отбора на прямые запросы
			//МассивДокументов = ЗарплатаКадры.ДокументыПоФизическомуЛицу(ДанныеСотрудника.ФизическоеЛицо);
			МассивДокументов = ЗарплатаКадры.ДокументыПоФизическомуЛицу(ДанныеСотрудника.ФизическоеЛицо, ЭтаФорма.ИмяФормы);
			// --- 
			ОбщегоНазначенияКлиентСервер.УстановитьПараметрДинамическогоСписка(Список, "МассивДокументов", МассивДокументов, Истина);
			УстановитьТипыОбъектовОповещения();
			
			ОбщегоНазначенияКлиентСервер.УстановитьСвойствоЭлементаФормы(
				Элементы,
				"Сотрудник",
				"Видимость",
				Ложь);
			
			Если Не ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ДанныеСотрудника.ГоловнаяОрганизация, "ЕстьОбособленныеПодразделения") Тогда
				
				ОбщегоНазначенияКлиентСервер.УстановитьСвойствоЭлементаФормы(
					Элементы,
					"Организация",
					"Видимость",
					Ложь);
				
			КонецЕсли;
			
		КонецЕсли;
		
	Иначе
		
		ОписаниеТипаФизическоеЛицо = Новый ОписаниеТипов("СправочникСсылка.ФизическиеЛица");
		СтруктураПараметраФизическоеЛицо = Новый Структура("ТипПараметра, ИмяПараметра", ОписаниеТипаФизическоеЛицо, "МассивДокументов");
		СтруктураПараметровОтбора = Новый Структура("Сотрудник", СтруктураПараметраФизическоеЛицо);
		ЗарплатаКадры.ПриСозданииНаСервереФормыСДинамическимСписком(ЭтотОбъект, "Список", "СписокНастройкиОтбора",
			СтруктураПараметровОтбора, "СписокКритерииОтбора");
		
	КонецЕсли;
	
	ОбщегоНазначенияКлиентСервер.УстановитьСвойствоЭлементаФормы(Элементы, "ФормаУтвердить", "Видимость", Пользователи.РолиДоступны("ДобавлениеИзменениеДанныхДляНачисленияЗарплатыРасширенная, ДобавлениеИзменениеНачисленнойЗарплатыРасширенная"));
	ЗарплатаКадрыРасширенный.УстановитьУсловноеОформлениеСпискаМногофункциональныхДокументов(ЭтаФорма);
	
	ЗарплатаКадрыРасширенный.СформироватьПодменюСоздатьФормыСпискаДокументов(ЭтаФорма, "ЖурналДокументов.КадровыеДокументы");
	
	// СтандартныеПодсистемы.ПодключаемыеКоманды
	ПараметрыРазмещения = ПодключаемыеКоманды.ПараметрыРазмещения();
	ПараметрыРазмещения.КоманднаяПанель = Элементы.КоманднаяПанельФормы;
	
КонецПроцедуры

&НаСервере
Процедура ОбновитьНаСервере()
	
	Если ЗначениеЗаполнено(СотрудникСсылка) Тогда
		// +++ замена критерии отбора на прямые запросы
		//МассивДокументов = ЗарплатаКадры.ДокументыПоФизическомуЛицу(ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СотрудникСсылка, "ФизическоеЛицо"));
		МассивДокументов = ЗарплатаКадры.ДокументыПоФизическомуЛицу(ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СотрудникСсылка, "ФизическоеЛицо"), ЭтаФорма.ИмяФормы);
		// --- 
		ОбщегоНазначенияКлиентСервер.УстановитьПараметрДинамическогоСписка(Список, "МассивДокументов", МассивДокументов, Истина);
	Иначе
		ЗарплатаКадры.ОбновитьФормыСДинамическимСписком(ЭтотОбъект);
	КонецЕсли;
	
КонецПроцедуры

Журнал документов

Критерий отбора

ЗУП 3.1 КОРП тормозит медленная оптимизация конструктор динамический запрос журнал документов критерии отбора доработка типовых RLS отбор динамический список избыточное

См. также

HighLoad оптимизация Системный администратор Программист Бесплатно (free)

Небольшая история о проблемах производительности из-за нехватки процессорных мощностей. А также описание основных показателей работы CPU.

26.11.2020    13692    Infostart    21    

137

HighLoad оптимизация Системный администратор Программист Бесплатно (free)

Периодически занимаясь исследованиями производительности я повидал много решений. Делюсь некоторыми выводами на основании теста Гилева и собственных мыслей.

25.05.2020    62836    starik-2005    251    

62

HighLoad оптимизация Программист Бесплатно (free)

При построении мощных производительных отказоустойчивых решений для интеграции во всем мире активно используются технологии обработки очередей сообщений с помощью брокера RabbitMQ и кэш-сервера Redis. О практическом опыте использования этих технологий при построении ИТ-ландшафта, включающего системы на 1С, на конференции Infostart Event 2019 Inception рассказал Сергей Наумов.

12.05.2020    19672    SergeyN    3    

61

HighLoad оптимизация Нейросети Системный администратор Программист Бесплатно (free)

Проводить мониторинг производительности вручную, выявляя закономерности в куче графиков и десятках таблиц, довольно сложно. Но это не значит, что разбираться с инцидентами нужно только после жалоб от пользователей. О том, как обучить нейронную сеть и заставить ее оповещать о проблемах, на конференции Infostart Event 2019 Inception рассказал начальник сектора разработки ООО «Группа Полипластик» Владимир Крючков.

27.04.2020    9028    ivanov660    6    

40

Технологический журнал HighLoad оптимизация Программист Бесплатно (free)

Примеры bash - скриптов для поиска ошибок в технологическом журнале.

23.04.2020    8288    vasilev2015    7    

40

HighLoad оптимизация Системный администратор Программист Бесплатно (free)

Описание и руководство "Мониторинг производительности": краткое описание конфигурации, сборник из статей, примеров - собрано в одном файле.

21.04.2020    11699    ivanov660    6    

63

HighLoad оптимизация WEB-интеграция Мобильная разработка Администрирование веб-серверов Системный администратор Программист Платформа 1С v8.3 Бесплатно (free)

В этой статье я расскажу о проблемах бека для мобильных приложений или другого фронта, который требует производительности, быстрой реакции и отказоустойчивости, и как я решил это благодаря opensource проекту PostgREST и СУБД Postgre SQL 12. Проведу простой тест производительности для сравнения 1С с данным решением. Это может быть полезно всем, кто разрабатывает мобильные приложения либо фронтсайд-приложения для 1С на чем угодно - на мобильной платформе или на нативном языке или на Simple UI. И также обзор новых функций SimpleUI для связи с этим бекендом.

31.03.2020    22372    informa1555    35    

154
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. PerlAmutor 158 04.01.18 18:56 Сейчас в теме
В ERP тоже есть критерии отборов и к счастью их мало, а в последней версии так еще меньше. На сертификационном экзамене по платформе есть следующие вопросы:

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

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

Из чего можно сделать вывод, что объект "критерий отбора" сам по себе - анахронизм, оставленный для совместимости со старыми конфигурациями под обычное приложение.
2. Iaskeliainen 394 04.01.18 21:04 Сейчас в теме
(1) да, у меня были мысли, что 1С:ЗУП 3 писали те же, что и 1С:ЗУП 2.5.
3. MrWonder 651 10.01.18 09:06 Сейчас в теме
Алексей! Результат, безусловно, хороший. Это видно на продуктивной среде. Но вопрос поведения остаётся непонятным.
Оставьте свое сообщение