Решение проблемы ПроверитьВывод() для огромных документов

06.11.17

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

Пример того, как я ускорил вывод большого табличного документа(100+ страниц)

Скачать файл

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Наименование SM По подписке [?] Купить один файл
Инвентаризационная опись в разрезе головных изделий
.epf 33,91Kb
3
3
2 SM
Скачать Купить за 2 150 руб.

Каких результатов удалось достичь

Изначально, печатная форма, содержащая ~200 страниц выводилась(речь идет об именно выводе в табличный документ) > 15 минут. В результате оптимизации удалось сократить время до 1-2 минут.

Последовательность действий:

  1. Объявляем ТабличныйДокумент как ранее, настраиваем его свойства(Масштаб, ориентация страницы, поля);
  2. Объявляем еще один табличный документ, назовем его ИтоговыйТабличныйДокумент, копируем в него все свойства ранее созданного;
  3. Выполняем вывод одной страницы в ТабличныйДокумент, проверяем, умещается ли он или нет с помощью метода ПроверитьВывод()
  4. Когда вывод не умещается на странице ТабличныйДокумент, то:
    1. Выполняем его вывод в ИтоговыйТабличныйДокумент методом Вывести(ТабличныйДокумент)
    2. Очищаем ТабличныйДокумент
    3. Выводим шапку(если требуется) в Табличный документ
  5. Пункты 3-4 повторяются в цикле, как и при обычном формировании печатных форм
  6. В конце формирования выводим подвал в ИтоговыйТабличныйДокумент

Как это выглядит в программе

    ТабДокумент = Новый ТабличныйДокумент;
    ТабДокумент.ИмяПараметровПечати = "ПАРАМЕТРЫ_ПЕЧАТИ_ИнвентаризационнаяОписьВРазрезеГоловныхИзделий";
    ТабДокумент.ОриентацияСтраницы  = ОриентацияСтраницы.Портрет;
    ТабДокумент.МасштабПечати = 90;
    Макет   = ПолучитьМакет("ИнвентаризационнаяОпись");
    
    // Документ используется для вывода по страницам
    ИтоговыйТабДок = Новый ТабличныйДокумент;
    ЗаполнитьЗначенияСвойств(ИтоговыйТабДок, ТабДокумент); 
    
    НомерСтрокиНачало = ТабДокумент.ВысотаТаблицы + 1;
    Запрос = Новый Запрос;
    Запрос.Текст=
    "ВЫБРАТЬ
    |    ИнвентаризацияНЗП.Организация,
    |    ИнвентаризацияНЗП.Подразделение,
    |    ИнвентаризацияНЗП.Ссылка,
    |    ИнвентаризацияНЗП.Номер,
    |    ИнвентаризацияНЗП.Дата,
    |    ИнвентаризацияНЗП.Заказ,
    |    ИнвентаризацияНЗП.ВводитьЗаказыПоСтрокам,
    |    ИнвентаризацияНЗП.Заказ.Представление КАК ЗаказПредставление,
    |    ИнвентаризацияНЗП.Организация.Представление КАК ОрганизацияПредставление,
    |    ИнвентаризацияНЗП.Подразделение.Представление КАК ПодразделениеПредставление
    |ИЗ
    |    Документ.ИнвентаризацияНЗП КАК ИнвентаризацияНЗП
    |ГДЕ
    |    ИнвентаризацияНЗП.Ссылка = &ТекДок";
    Запрос.УстановитьПараметр("ТекДок", СсылкаНаОбъект);
    
    Шапка = Запрос.Выполнить().Выбрать();
    Шапка.Следующий();
    
    ИнфоОрг = УправлениеКонтактнойИнформацией.СведенияОЮрФизЛице(Шапка.Организация, СсылкаНаОбъект.Дата);
    
    Область = Макет.ПолучитьОбласть("Шапка");
    Область.Параметры.ПечНомерДок      = Шапка.Номер;
    Область.Параметры.ПечДатаДок       = Шапка.Дата;
    Область.Параметры.ПечОрганизация   = ИнфоОрг.ПолноеНаименование;
    Область.Параметры.ПечПодразделение = Шапка.ПодразделениеПредставление;
    
    Область.Параметры.КодОКПО          = ИнфоОрг.КодПоОКПО;
    Область.Параметры.Организация      = Шапка.Организация;
    Область.Параметры.Подразделение    = Шапка.Подразделение;
    
    ТабДокумент.Вывести(Область);
    ОбластьТабШапка = Макет.ПолучитьОбласть("ТабШапка");
    ТабДокумент.Вывести(ОбластьТабШапка);
    
    Индекс       = 0;
    ВсегоТМЦ     = 0;
    ВсегоКолФакт = 0;
    ВсегоКолОПУ  = 0;
    ВсегоКолОтк  = 0;
    КолФактСтр   = 0;
    СтрокНаЛисте = 5;// Изменим, подбирается опытным путем
    НПП = 0;
    ВсегоСтраниц = 0;    
    
    
    ВсегоИзделий = ДанныеДляПечати.Строки.Количество();
    
    ОбластьПодвал = Макет.ПолучитьОбласть("ПодвалСтраницы");
    
    Для Каждого СтрокаДЗ1 Из ДанныеДляПечати.Строки Цикл
        
        Состояние("Производится вывод печатной формы (изделие "+ (ДанныеДляПечати.Строки.Индекс(СтрокаДЗ1) + 1) + "/" + ВсегоИзделий + "). Стр. " + ВсегоСтраниц);
        
        Область  = Макет.ПолучитьОбласть("СтрокаПродукция");
        Область.Параметры.ПечНоменклатура = СтрокаДЗ1.НоменклатураПредставление;
        Область.Параметры.Номенклатура    = СтрокаДЗ1.Номенклатура;
        Область.Параметры.ПечКолБух          = Формат(СтрокаДЗ1.Количество, "ЧЦ=15; ЧДЦ=3");
        Область.Параметры.ПереченьЗаказов = СтрокаДЗ1.ПереченьЗаказов;
        ТабДокумент.Вывести(Область);
        
        Для Каждого СтрокаДЗ2 Из СтрокаДЗ1.Строки Цикл
            
            // Делаем через проверить вывод
            Индекс   = Индекс + 1;
            ВсегоТМЦ = ВсегоТМЦ + 1;
            НПП      = НПП + 1;
            Область  = Макет.ПолучитьОбласть("Строка");
            Область.Параметры.ПечНомер        = Формат( НПП, "ЧЦ=6; ЧН=");
            Область.Параметры.ПечНоменклатура = СтрокаДЗ2.НоменклатураПредставление;
            Область.Параметры.Номенклатура    = СтрокаДЗ2.Номенклатура;
              
            
            //Область.Параметры.ПечШифрИВЦ      = Шифр;
            Область.Параметры.ПечКодТМЦ       = СокрЛП(СтрокаДЗ2.НоменклатураКод);
            Область.Параметры.ПечКодЕИ        = СтрокаДЗ2.ЕдиницаИзмеренияКод;
            Область.Параметры.ПечЕИ           = СтрокаДЗ2.ЕдиницаИзмерения;
            
            Область.Параметры.ПечКолБух       = Формат(СтрокаДЗ2.Количество, "ЧЦ=15; ЧДЦ=3");
            
            // Если с подвалом не умещается - выводим подвал, начинаем новую страницу
            МассивДляПроверки = Новый Массив;
            МассивДляПроверки.Добавить(Область);
            //МассивДляПроверки.Добавить(ОбластьПодвал);
            Если ТабДокумент.ПроверитьВывод(МассивДляПроверки) = ЛОЖЬ Тогда
                
                // Подвал страницы просили не выводить
                //ОбластьПодвал.Параметры.КолНомСтр  = Индекс;
                //ОбластьПодвал.Параметры.ИтогКолСтр = КолФактСтр;
                //ТабДокумент.Вывести( ОбластьПодвал);
                Индекс     = 0;
                КолФактСтр = 0;
                ТабДокумент.ВывестиГоризонтальныйРазделительСтраниц();
                СтрокНаЛисте = 10; // Изменено - подбирается опытным путем  Это было раньше (На первом листе 9 строк, на остальных 30)
                
                ВсегоСтраниц = ВсегоСтраниц + 1;
                
                // Переносим изменения в итоговый документ, очищаем оригинал, выводим шапку на след.странице
                ИтоговыйТабДок.Вывести(ТабДокумент);
                
                ТабДокумент.Очистить();
                ТабДокумент.Вывести(ОбластьТабШапка);
                
            КонецЕсли;

            ОбработкаПрерыванияПользователя();// На всякий случай
            
            ТабДокумент.Вывести(Область);
            
            КолФактСтр   = КолФактСтр   + СтрокаДЗ2.Количество;
            ВсегоКолФакт = ВсегоКолФакт + СтрокаДЗ2.Количество;

        КонецЦикла;
        
    КонецЦикла;
    
    Область = Макет.ПолучитьОбласть("Итого");
    Область.Параметры.ПечКолФактИтого = Формат( ВсегоКолФакт, "ЧЦ=15; ЧДЦ=3");
    
    // Подвал страницы просили не выводить
    //ТабДокумент.Вывести( Область);
    //Если Индекс > 0 Тогда
    //    ОбластьПодвал = Макет.ПолучитьОбласть("ПодвалСтраницы");
    //    ОбластьПодвал.Параметры.КолНомСтр  = Индекс;
    //    ОбластьПодвал.Параметры.ИтогКолСтр = КолФактСтр;
    //    ТабДокумент.Вывести( ОбластьПодвал);
    //КонецЕсли;
    
    Область = Макет.ПолучитьОбласть("Подвал");
    Область.Параметры.ВсегоТМЦ      = ВсегоТМЦ;
    Область.Параметры.ИтогоКолОпись = ВсегоКолФакт;
    ТабДокумент.ВывестиГоризонтальныйРазделительСтраниц();
    ТабДокумент.Вывести( Область);
    //УправлениеПечатью.ЗадатьОбластьПечатиДокумента(ТабДокумент, НомерСтрокиНачало, Новый СписокЗначений, СсылкаНаОбъект);
    
    // Выведем всяческие подвалы
    ИтоговыйТабДок.Вывести(ТабДокумент);

Бонусом идет печатная форма для УПП 1.3, в которой присутствует этот кусок кода

См. также

HighLoad оптимизация Программист Платформа 1С v8.3 Бесплатно (free)

Метод очень медленно работает, когда параметр приемник содержит намного меньше свойств, чем источник.

06.06.2024    7152    Evg-Lylyk    60    

41

HighLoad оптимизация Программист Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Анализ простого плана запроса. Оптимизация нагрузки на ЦП сервера СУБД используя типовые индексы.

13.03.2024    4230    spyke    28    

48

HighLoad оптимизация Программист Платформа 1С v8.3 Бесплатно (free)

Оказывается, в типовых конфигурациях 1С есть, что улучшить!

13.03.2024    6430    vasilev2015    20    

40

HighLoad оптимизация Инструменты администратора БД Системный администратор Программист Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

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

2 стартмани

15.02.2024    10085    206    ZAOSTG    74    

110

HighLoad оптимизация Системный администратор Программист Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

Принимать, хранить и анализировать показания счетчиков (метрики) в базе 1С? Почему бы нет? Но это решение быстро привело к проблемам с производительностью при попытках построить какую-то более-менее сложную аналитику. Переход на PostgresSQL только временно решил проблему, т.к. количество записей уже исчислялось десятками миллионов и что-то сложное вычислить на таких объемах за разумное время становилось все сложнее. Кое-что уже практически невозможно. А что будет с производительностью через пару лет - представить страшно. Надо что-то предпринимать! В этой статье поделюсь своим первым опытом применения СУБД Clickhouse от Яндекс. Как работает, что может, как на нее планирую (если планирую) переходить, сравнение скорости работы, оценка производительности через пару лет, пример работы из 1С. Все это приправлено текстами запросов, кодом, алгоритмами выполненных действий и преподнесено вам для ознакомления в этой статье.

1 стартмани

24.01.2024    4338    glassman    17    

39

HighLoad оптимизация Программист Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

Встал вопрос: как быстро удалить строки из ТЗ? Рассмотрел пять вариантов реализации этой задачи. Сравнил их друг с другом на разных объёмах данных с разным процентом удаляемых строк. Также сравнил с выгрузкой с отбором по структуре.

09.01.2024    9091    doom2good    49    

69
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. Armando 1400 06.11.17 22:43 Сейчас в теме
На 8.3.11 не проверяли? Там что-то оптимизировали на эту тему:

Оптимизирована работа метода табличного документа ПроверитьВывод().
Источник: http://downloads.v8.1c.ru/content//Platform/8_3_11_2700/V8Update.htm#0bacf61e-057e-11e7-a3f7-0050569f678a
SITR-utyos; +1 Ответить
2. SITR-utyos 1426 07.11.17 07:08 Сейчас в теме
(1) Нет, на 8.3.11 не проверял. Последняя платформа, что у меня стоит 8.3.10.2639 - там такая же проблема.
Спасибо, за заметку
3. mifka186 9 07.11.17 09:41 Сейчас в теме
Что-то подобное в расчетном листке организации ЗУП 2.5 используется.

Если ВыводитьПоСтраницам Тогда
			
	ОбластьПриемник = ТабличныйДокументДляПроверки.Область(ТабличныйДокументДляПроверки.ВысотаТаблицы + 1, ,ТабличныйДокументДляПроверки.ВысотаТаблицы + (НомерСтрокиКонцаРЛ - НомерСтрокиНачалаРЛ) + 1, );
	ТабличныйДокументДляПроверки.ВставитьОбласть(ОбластьРЛ, ОбластьПриемник, , истина);
		
	КоличествоСтраниц = 0;
	Попытка
		КоличествоСтраниц = ТабличныйДокументДляПроверки.КоличествоСтраниц();
	Исключение
	КонецПопытки;
			
	Если КоличествоСтраниц > 1 тогда
				
		ТабличныйДокумент.Область(НомерСтрокиНачалаРЛ, , НомерСтрокиНачалаРЛ,).НачалоСтраницы = истина;
				
		ТабличныйДокументДляПроверки.Очистить();
			
		Для сч = 0 по НастройкаШириныКолонокРасчетногоЛистка.ВГраница() Цикл
			ОбластьКолонкиРЛ                  = ТабличныйДокументДляПроверки.Область(, сч+1, , сч+1);
			ОбластьКолонкиРЛ.ШиринаКолонки    = НастройкаШириныКолонокРасчетногоЛистка[сч];
			ОбластьКолонкиРЛ.РазмещениеТекста = ПолучитьТипРазмещенияТекста(НастройкаРазмТекстаКолонокРасчетногоЛистка[сч]);
		КонецЦикла;
				
		ОбластьПриемник = ТабличныйДокументДляПроверки.Область(ТабличныйДокументДляПроверки.ВысотаТаблицы + 1, ,ТабличныйДокументДляПроверки.ВысотаТаблицы + (НомерСтрокиКонцаРЛ - НомерСтрокиНачалаРЛ) + 1, );
		ТабличныйДокументДляПроверки.ВставитьОбласть(ОбластьРЛ, ОбластьПриемник, , истина);
				
	КонецЕсли;
			
КонецЕсли;
Показать
4. madonov 247 07.11.17 09:49 Сейчас в теме
Плюс за идею. Взял на вооружение.
5. nvv1970 07.11.17 11:14 Сейчас в теме
Работа с табличным документом, текстовым документом деградирует пропорционально росту его объема. Поэтому можно применять не только указанный метод, но и использовать для организации печати страниц двумерный массив табличных документов. Так можно определить последовательность вывода по строкам, столбам, чёт/нечет и т.д. на сколько хватит фантазии.
sm.artem; +1 Ответить
6. BigB 193 07.11.17 12:09 Сейчас в теме
Каждый месяц генерирую примерно > 600 тысяч квитанций пачками по 1000 листов в пачке.
На каждой странице (внизу) есть адрес доставки и кому.
Квитанция должна занимать не более одной страницы.
Я поступаю так: если адрес доставки больше 150 тогда делаю проверку на количество страниц и если произошел перенос адреса на другую страницу - уменьшаю шрифт, пока не влезет в одну страницу.
Вот такой кусок кода у меня:
//Автоматическое изменение размера шрифта адреса доставки
Если СтрДлина(Выборка.АдресДоставки)>150 И ТДП.КоличествоСтраниц()>ХХ Тогда
	Y=Формат(ТДП.Области.Подвал.Низ,"ЧГ=0");
	ОбластьДоставки=ТДП.Область("R"+Y+"C2:R"+Y+"C10");
	Пока Истина Цикл
		ОбластьДоставки.Шрифт=Новый Шрифт(ОбластьДоставки.Шрифт,ОбластьДоставки.Шрифт.Имя,ОбластьДоставки.Шрифт.Размер-1);
		Если ТДП.КоличествоСтраниц()=ХХ Тогда
			Прервать
		ИначеЕсли ОбластьДоставки.Шрифт.Размер<7 Тогда
			Сообщить("Л/С "+Выборка.НомерЛС+" Не удалось разместить на одной странице адрес доставки:"+Символы.ПС+Выборка.АдресДоставки);
			Прервать
		КонецЕсли;
	КонецЦикла;
КонецЕсли;
Показать
cargobird; jaroslav.h; sm.artem; AlexGroovy; Danil.Potapov; +5 Ответить
7. vis_tmp 32 08.11.17 06:33 Сейчас в теме
Спасибо, интересный метод ускорения печати больших отчётов!
8. AlexGroovy 08.11.17 11:11 Сейчас в теме
А где именно в коде оптимизация ? Я понял,если бы вы избежали ПроверитьВывод(),но у он у вас остался и находится в цикле.Смысл в том,что всё выводится в итоговый табличный документ?
9. корум 288 08.11.17 11:52 Сейчас в теме
(8)
А где именно в коде оптимизация ? Я понял,если бы вы избежали ПроверитьВывод(),но у он у вас остался и находится в цикле.Смысл в том,что всё выводится в итоговый табличный документ?

нет, не понял.
Смысл в том, что не тормозит.
ПроверитьВывод() 3 страниц 0,01 сек, ПроверитьВывод() 300 страниц не 1 сек, а 100.
ПроверитьВывод() для 3 страниц 100 раз - 1 сек.
collider; user717534; madonov; +3 Ответить
11. AlexGroovy 08.11.17 14:13 Сейчас в теме
А как у вас получается область на 300 страниц сразу,если вы в цикле как и в типовом механизме добавляете по области строчке и сразу переносите другие на новую страницу?
13. madonov 247 09.11.17 02:36 Сейчас в теме
(11) да не область на 300 страниц. А сам табличный документ на 300 страниц.

БольшойТабличныйДокумент.ПроверитьВывод(Область)
работает гозадо медленнее чем
МалыйТабличныйДокумент.ПроверитьВывод(Область)

Автор предлагает не вызывать ПроверитьВывод() для большого документа на 300 страниц, а вызывать эту функцию только для малого табличного документа, который равен последней странице большого.

Ускорение происходит не за счет того, что уменьшается количество вызовов функции ПроверитьВывод(), а за счет того, что каждый вызов занимает в разы меньше времени.
collider; Rabot; OksanaVl; starik-2005; echo77; klinval; корум; AlexGroovy; +8 Ответить
14. AlexGroovy 09.11.17 09:54 Сейчас в теме
(13)Спасибо большое за ответ =)
10. BigB 193 08.11.17 12:35 Сейчас в теме
(0) Уберите из названия "для огромных документов".
Поверьте мне - 200 страниц это мизерный документ.
Не используйте ПроверитьВывод().
Вместо этого используйте КоличествоСтраниц() в разумных пределах и отчет будет формироваться очень быстро.
12. madonov 247 09.11.17 02:28 Сейчас в теме
(10) предположим, что выводим область и при помощи КоличествоСтраниц() видим, что количество страниц увеличилось.
И как без ПроверитьВывод() определить целиком ли новая новая область на новой странице или её "разорвало" на разные и требуется вставка разрыва страницы?
15. zhuravlev_as 400 09.11.17 09:59 Сейчас в теме
Идея интересная. Надо запомнить
16. androgin 10.11.17 00:55 Сейчас в теме
Простите, а если длина наименования постоянно разная и может занимать 1,2,3 строки, то как вы это обойдете?
На одном листе может быть 40 строк, а следующем 50, а третьем ваще 35.
Тут уже параметр СтрокНаЛисте не имеет смысла
17. echo77 1865 10.11.17 05:59 Сейчас в теме
(16) В примере переменная СтрокНаЛисте нигде не используется, если посмотреть циклы внимательное. Это наследие типовой формы инвентаризации - там сделано именно так
Оставьте свое сообщение