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

Публикация № 722861

Администрирование - Оптимизация БД (HighLoad)

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

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

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

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

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

30

См. также

Специальные предложения

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

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

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

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