gifts2017

Подсчет количества элементов в списке иерархического справочника, при изменении отбора

Опубликовал Xer shi (Xershi) в раздел Программирование - Практика программирования

Допустим, у вас появилась хотелка: знать точное количество элементов справочника в группах (папках).
С чего начать? Конечно, запрос, но не простой! Будем использовать построитель запроса и, мало того, с ним будет использоваться динамический отбор!

Что было:

А. База на платформе 8.3.5.1248 обычные формы.

Б. В базе список справочника "Номенклатура" иерархический.

Что нужно:

Подсчет количества элементов в списке справочника "Номенклатура", при изменении отбора.

Решение:

Для решения нам понадобится:

1. В список справочника "Номенклатура" добавить колонку "КоличествоЭлементов" (вы можете назвать её как вам нужно, но не забудьте потом изменить код).

2. Для элемента формы "Список" определить процедуру:

 

Процедура СписокПриПолученииДанных(Элемент, ОформленияСтрок)
    
    Для Каждого ОформлениеСтрок Из ОформленияСтрок Цикл
        Если ОформлениеСтрок.ДанныеСтроки.ЭтоГруппа Тогда
            ОформлениеСтрок.Ячейки.КоличествоЭлементов.Значение = ПолучитьКоличествоЭлементов(ОформлениеСтрок.ДанныеСтроки.Ссылка);   
        Иначе
            ОформлениеСтрок.Ячейки.КоличествоЭлементов.Значение = 1;
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

3. Осталось посчитать в функции количество самих элементов:

 

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

    КоличествоЭлементов = Выборка.Количество();      
    
    Возврат КоличествоЭлементов;
    
КонецФункции

Результат:

Код написали. Отбор работает. Результат производительности при открытии одно из папок: 0,360265.

Казалось бы, отлично, задача решена! Но это решение можно оптимизировать!

И сейчас я покажу, как улучшить результат в разы!

 

Решение2:

1. Без изменений.

2. Допишем условие, чтобы код работал только тогда, когда колонка "КоличествоЭлементоввидима:

 

Процедура СписокПриПолученииДанных(Элемент, ОформленияСтрок)
	
	ВидимостьКоличествоЭлементов = ЭлементыФормы.Список.Колонки.КоличествоЭлементов.Видимость;
	
	Для Каждого ОформлениеСтрок Из ОформленияСтрок Цикл
		Если ВидимостьКоличествоЭлементов Тогда
			Если ОформлениеСтрок.ДанныеСтроки.ЭтоГруппа Тогда
				ОформлениеСтрок.Ячейки.КоличествоЭлементов.Значение = ПолучитьКоличествоЭлементов(ОформлениеСтрок.ДанныеСтроки.Ссылка);	
			Иначе
				ОформлениеСтрок.Ячейки.КоличествоЭлементов.Значение = 1;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

3. Оптимизируем запрос, уменьшив количество обращений к базе (обратите внимание на условие в запросе и путь к данным для отбора):

 

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

 

Результат2:

Теперь наш код стал оптимальнее. Отбор работает, как и должен. А вот результат производительности: 0,113300.

Нам удалось улучшить результат в разы, и это отлично! 

Задача решена на 100%!

Благодарности:

Исходный код был подсмотрен тут. В оптимизации решения помог Молодов Алексей!

Добавил текстовый файл с кодом двух решений! Всем удачи!

Скачать файлы

Наименование Файл Версия Размер
Решение 1
.txt 4,71Kb
07.10.15
1
.txt 4,71Kb Скачать

См. также

Подписаться Добавить вознаграждение
Комментарии
1. Serj (Serj1C) 08.10.15 12:26
> Казалось бы, отлично, задача решена! Но это решение можно оптимизировать!
Запрос в цикле - это ужасно.
Необходимо собрать все видимые группы, выполнить один запрос по этим группам и потом заполнять колонку.
2. Xer shi (Xershi) 08.10.15 13:29
(1) Serj1C, поделитесь, как вы этот список будете получать?
3. Александр Губанов (gubanoff) 06.11.15 14:32
(2) Xershi, заполняете массив, перебрав все строки в событии ПриПолученииДанных в цикле
Для Каждого ОформлениеСтрок Из ОформленияСтрок Цикл
. Потом этот массив передаете в функцию для подсчета. Потом еще раз проходите в цикле
Для Каждого ОформлениеСтрок Из ОформленияСтрок Цикл
и выводите значения. Сам запрос и его текст закешируйте в переменную и инициализируйте один раз при открытии. Построитель запроса тоже не надо, он потяжелее будет, чем просто запрос. Плюс все это дело можно делать, только если пользователь установил видимость колонки, т.к. это может быть не всем нужно. Не благодарите.
4. Xer shi (Xershi) 06.11.15 14:39
(3) gubanoff, да уже давно подметил что "ОформленияСтрок" это обычная коллекция, которая и содержит нужный список.
Построитель запроса не просто так использовался, так что тут только ускорение получится на массиве. Видимость колонки и так в условии стоит, это вы похоже тоже пропустили.
Может сделаю 2 вариант уже с массивом. Правда пока вопрос не очень актуален.
5. Xer shi (Xershi) 02.12.15 10:20
(3) gubanoff, провел исследование http://forum.infostart.ru/forum86/topic142222/. Результат поразил запрос не оптимален! И 43 (оптимальных) запроса выполняются быстрее 3(не оптимальных) при прочих равных условиях!