Случай с объединением таблиц

У одного из клиентов в отраслевой конфигурации, которая работает еще на платформе 8.3.9.2170 возникла ошибка, когда они пытались объединить данные за 12 месяцев в одну таблицу:

Объемы данных были большими, прежде чем ошибка была выдана, прошло 2 часа. Поэтому на отладку у меня было не очень много итераций, если конечно, не моделировать ситуацию на маленьких выборках.

К счастью, я сразу обнаружил узкое место в коде.

Дело в том, что каждая таблица из базы загружалась в таблицу значений в памяти, а потом объединялась в одну общую, объединенную таблицу:

ТЗ_РезультатОбъединения = МассивТЗДляОбъединения[0];
Для к = 1 по МассивТЗДляОбъединения.Количество()-1 Цикл
	ТЗ_РезультатОбъединения = СложитьТЗ(ТЗ_РезультатОбъединения,МассивТЗДляОбъединения[к]);
КонецЦикла;
ТЗ_РезультатОбъединения.ЗаполнитьЗначения(НоваяДатаЗагрузки,"ДатаЗагрузки");

А само сложение видимо в целях «быстродействия» делалось по заповедям 1С через виртуальные таблицы:

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

На этом 1С и валилась.

Самое удивительное, что я обнаружил, что дальнейший код обработки этой объединенной супер-таблицы можно переписать так, чтобы программа обращалась по очереди к каждой строке каждой из загруженной в память таблицы, т.е. объединять в одну таблицу нет смысла.

Кстати, в коде запись объединенной таблицы в базу данных была организована пачками по 1000 элементов, видимо, разработчики уже натыкались на ограничение, что большие наборы данных не записываются.

Закоментированный код — как было до, его заменяет мой новый код. Обратите внимание, как элегантно организован обход таблиц.

Мне повезло, что в каждой таблице были одинаковые колонки, иначе код обработки был бы сложнее.

//+++ не объединяем таблицы, заполняем колонку
Для каждого ТЗКаждая ИЗ МассивТЗДляОбъединения Цикл
	ТЗКаждая.ЗаполнитьЗначения(НоваяДатаЗагрузки,"ДатаЗагрузки");
КонецЦикла;
//---

//+++ Берем колонки из первой подходящей таблицы
//КопияТЗ = ТЗ_РезультатОбъединения.СкопироватьКолонки(); //
КопияТЗ = ТЗКаждая.СкопироватьКолонки(); 
//--- 

НомерПоПорядку = 0;
КоличествоПачек = 0;

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

Вот так вот иногда попытки оптимизировать приводят к проблемам производительности и масштабирования.

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

Объем: 1.5 час

fixin

Программирую на 1С с 1999 года. В 1С просто Гений. В 2020 году ушел из офиса на вольные хлеба фриланса. Принимаю заказы.

Читайте также:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *