1с найти строку в динамическом списке
Здесь Вы получаете доступ к публикациям, в которых профессионалы делятся своим уникальным опытом. Для Вас мы собрали более 30 000 различных материалов по 1С.
Рейтинг: 1105
И все же в этом черном ящичке (динамическом списке) есть узкие места, которые влияют на производительность. Попробую описать пойманные места. Запросы в динамическом списке просты, с одной основной таблицей и без фактических соединений на уровне языка 1С.
Платформа воспроизведения: 1С:Предприятие 8.3 (8.3.6.2237)
Конфигурация: Управление торговлей, редакция 11.1 (11.1.9.56)
Процессор: 2х Intel Xeon X5660 2.8 ГГц
ОС:Win server 2008 R2 enterprise SP1 64x
MS SQL Server 2012. Выделено памяти 50 ГБ.
1С Сервер 64 (8.3.6.2237)
Все статистики и индексы абсолютно обновлены + полнотекстовые. В базе только один я, то есть никаких данных не добавляется и не изменяется. Никаких больше фонновых заданий не запущено.
Вступная часть:
Все начинается как обычно с маленького вопроса и как обычно перерастает в целый ряд вопросов "почему?".
У пользователей было замечено частое подвисание ДС (динамический список). Со слов пользователя, крутятся часики и программа замирает.
Расследование:
Долго не пришлось искать, поскольку конфигурация давно уже изменена (не типовая на 100%), то кто-то постарался и установил у пользователей автоматическое обновление в 10 секунд, при этом на уровне конфигурации. Если кто не в курсе, это находится здесь:
На этом не все, часики, конечно, мы устранили. Но в процессе поиска и оптимизации долгих запросов на сервисах Гилева, попадаются опять же наши запросы и наш список, но с использованием оператора поиска LIKE "%%", то есть пользователи осуществляли поиск по части строки. А сервис регистрирует какие-то космические цифры от 10 секунд до (кто бы подумал) 600 секунд. Усомниться в сервисе нет причин, проверено запросом в процессе работы пользователей:
Я понимаю вашу терпеливость и жажду "запрос в студию", но, обещаю, мы к этому обязательно подойдем, и они еще успеют надоесть :)
Ну вот, почти добрались, запускаем первую трассировку по ДС. Я для себя сделал открытие, ну я в принципе понимал модель работы ДС, но никогда не видел этого на уровне трассировки. Так вот, на этом уровне к базе идет четыри похожих запроса, разница между ними только в условиях. Для проверки приложил их тексты отдельно (Query1.txt - Query4.txt). Кстати, когда открыть список на форме впервые, то запрос будет только один.
Здесь, как видим, нет никаких операторов LIKE. Собственно, это правильно, поскольку это просто обновили список без условий. А теперь представляем ту ситуацию, что была прежде, уровень автообновления 10 секунд и поиск по части строки, который работает крайне интересно. То есть мы уже приблизились к тому, что нагружает сервер СУБД. Забегая наперед, сейчас вообще думаем у части пользователей поубирать автообновления ДС, зачем нагружать сервер лишними пакетами запросов.
После экспериментов на реквизиты, по которым делали пользователи поиск, накладываем Индексировать с доп. упорядочиванием, чем-таки вырываем частичную победу в производительности:
Одним из неприятных моментов при работе с динамическими списками управляемого приложения является невозможность использования такой привычной для обычного приложения опции, как «поиск по строке». Предлагаю один из вариантов реализации поиска по первым буквам в динамическом списке на основе общей Формы Подбора из типовой конфигурации УНФ.
В описанном ниже примере поиск вместе с сортировкой по полю реализован для колонок Код, Артикул, Наименование.
Добавляем Реквизиты формы ПоискКод, ПоискАртикул, ПоискНаименование с типом «Строка».Вносим их в Группу Поиск на форме.
Программируем события поля ввода АвтоПодбор:
процедура Установитьпорядок ( ИмяПоля )
Если НЕ ( СписокЗапасов . Порядок . элементы . количество () = 1 и СписокЗапасов . Порядок . элементы [ 0 ]. поле = Новый ПолеКомпоновкиДанных ( ИмяПоля )) тогда
СписокЗапасов . Порядок . элементы . очистить ();
НЭлемент = СписокЗапасов . Порядок . Элементы . Добавить ( Тип ( "ЭлементПорядкаКомпоновкиДанных" ));
НЭлемент . Использование = Истина;
НЭлемент . Поле = Новый ПолеКомпоновкиДанных ( ИмяПоля );
НЭлемент . ТипУпорядочивания = НаправлениеСортировкиКомпоновкиДанных . Возр ;
НЭлемент . РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных . Обычный ;
конецЕсли;
КонецПроцедуры
&НаСервере
Процедура УстановитьПоискПоСтрокеНаСервере ( Текст , ИмяКолонки )
ПостроительЗапроса = новый построительЗапроса ;
ПостроительЗапроса . Текст = "ВЫБРАТЬ
| Номенклатура.Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| ПОДСТРОКА(Номенклатура." + ИмяКолонки + " , 1, &Длина) = &Зн
| | Номенклатура.Ссылка.*>" ;
ПостроительЗапроса . Параметры . Вставить ( "Длина" , СтрДлина ( Текст ));
ПостроительЗапроса . Параметры . Вставить ( "Зн" , Текст );
Если Не СписокЗапасов . Параметры . элементы [ 2 ]. Значение = Справочники . Номенклатура . ПустаяСсылка () тогда
НЭлемент = ПостроительЗапроса . Отбор . Добавить ( "Ссылка" );
НЭлемент . ВидСравнения = ВидСравнения . ВИерархии ;
НЭлемент . Значение = СписокЗапасов . Параметры . элементы [ 2 ]. Значение ;
НЭлемент . Использование = Истина;
КонецЕсли;
ПостроительЗапроса . Выполнить ();
ТЗ = Построительзапроса . Результат . Выгрузить ();
Сп = новый СписокЗначений ;
Сп . ЗагрузитьЗначения ( ТЗ . ВыгрузитьКолонку ( "Ссылка" ));
СписокЗапасов . Отбор . Элементы . Очистить ();
НЭлемент = СписокЗапасов . Отбор . Элементы . добавить ( Тип ( "ЭлементОтбораКомпоновкиДанных" ));
Нэлемент . левоеЗначение = новый ПолекомпоновкиДанных ( "Номенклатура" );
НЭлемент . ВидСравнения = ВидСравненияКомпоновкиДанных . ВСписке ;
НЭлемент . ПравоеЗначение = Сп ;
НЭлемент . Использование = истина;
КонецПроцедуры
Таким образом получаем , что при наборе первых букв искомого варианта в поле «по артикулу» , например, группы поиска, происходит сортировка списка по колонке Артикул и отбор тех строк, артикул которых начинается с набранных символов.
Тут , конечно, был бы удачнее не отбор, а позиционирование на первом найденном в списке элементе . Т.е. мне бы хотелось в тексте запроса Построителя добавить «Первые 1» и дальше просто позиционироваться на найденной ссылке, но как найти позицию в динамическом списке ? Ответа на этот вопрос не нашла.
Буду рада любым предложениям по оптимизации этого варианта решения вопроса или ссылкам на другой вариант решения.
Многим в управляемых формах не хватает поиска по подстроке, который был реализован в обычных формах. Данная публикация описывает, как можно реализовать поиск в управляемых формах, который подобен механизму "поиска по подстроке" на стандартной панели обычных форм.
Пользователь кликают ячейку в колонку которая его интересует, когда решил что пора искать нажимает CTRL+S, набирает первые символы искомого объекта и далее нажимает вперед или назад. Поиск работает аналогично как обычных формах.
Краткое описание алгоритма:
Осуществляем выборку всех объектов, по правилам отбора которые настроены в динамическом списке и упорядочиваем наш результат выборки. Для того что бы результат запроса был пронумерован используем СКД с выводом в таблицу значений. Для того чтобы таблицу значений хранить на клиенте создаем реквизит на форме с типом таблица значений. Полученную таблицу из СКД, выгружаем в реквизит формы. И если пользователь нажал вперед то в динамическом списке делаем текущую строку, ссылку на следующий объект, после объекта активной строки из нашей таблицы значений в реквизите формы. И если пользователь нажал назад то в динамическом списке делаем текущую строку, ссылку на предыдущий объект, после объекта активной строки из нашей таблицы значений в реквизите формы.
Далее идет краткое описание пунктов на которые нужно обратить внимание когда вы будите писать аналогичный механизм на реальные управляемые формы. Эти пункты нельзя назвать инструкцией к написанию, вашего поиска, это скорее пункты по которым можно внести четкую ясность как работает код в опубликованной конфигурации на что можно ориентироваться при написании уже ваших алгоритмов.
1) Создаем форму списка
2) У формы списка создаем два реквизита
а) СтрокаПоиска с типом Строка
б) ВыборкаПоОбъект с типом таблица значений
3)На форме списка размещаем реквизит формы "СтрокаПоиска" в поле ввода и назначаем горячую клавишу "Ctrl+S".
5)В объекте, в нашем случае в номенклатуре создаем СКД с помощью которой будет формироваться список объектов по которым будет ходить курсор поиска. В СКД пишем запрос так что бы потом представлялось возможным открыть его в конструкторе запроса, а в коде заменить определенные подстроки и получить запрос к исполнению. А именно в время поиска важна Сортировка в списке объектов и то в какая ячейка сейчас выделена (по ней искать и нужно).
К примеру запрос
Номенклатура. Ссылка КАК Ссылка ,
Номенклатура.Родитель ,
Номенклатура.ОсновнойПоставщик.Наименование КАК ОсновнойПоставщик ,
Номенклатура.Наименование
ПОМЕСТИТЬ ТаблицаНоменклатуры
ИЗ
Справочник.Номенклатура КАК Номенклатура
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ТаблицаНоменклатуры.Ссылка ,
ТаблицаНоменклатуры.Родитель ,
ТаблицаНоменклатуры.ОсновнойПоставщик ,
ТаблицаНоменклатуры.Наименование
ИЗ
ТаблицаНоменклатуры КАК ТаблицаНоменклатуры
ГДЕ
( &актинаяЯчйка ПОДОБНО &СтрокаПоиска
ИЛИ ТаблицаНоменклатуры. Ссылка = &Ссылка )
УПОРЯДОЧИТЬ ПО
&ПараметрУПР
В этом запросе потом меняем
ЯчекааПоиска = "ТаблицаНоменклатуры." + АктинаяЯчейка ;
ТекстЗапроса = СхемаКомпановкиДанныхР . НаборыДанных . УпорядТаблица . Запрос ;
ТекстЗапроса = СтрЗаменить ( ТекстЗапроса , "&ПараметрУПР" , Порядок );
ТекстЗапроса = СтрЗаменить ( ТекстЗапроса , "&актинаяЯчйка" , ЯчекааПоиска );
СхемаКомпановкиДанныхР . НаборыДанных . УпорядТаблица . Запрос = ТекстЗапроса ;
Обращаю внимание что все колонки по которым будет осуществляться поиск, выбираем данные во временную таблицу и преобразуем их в строки для возможности дальнейшего сравнения.
5) программно переносим отборы настроенные в динамическом списке в нашу СКД поиска (этого пока не реализовано в примере, если будет актуально хотя бы нескольким пользователям то можно и доработать).
6) Так же в настройках СКД предусмотрительно добавляем возможные отборы, для иерархического справочника это будет Родитель элемента.
В случае если поиск в иерархическом справочнике, и отображение не списком то будет заполнять отбор
Если ПараметрыПоиска . Отображение <> "Список" тогда
// Натсройка отборов
НастройкиР . Отбор . Элементы [ 0 ]. Использование =истина;
НастройкиР . Отбор . Элементы [ 0 ]. ПравоеЗначение = ССылка . Родитель ;
7) У элемента формы СПИСОК создаем событие "СписокПриАктивизацииЯчейки" в нем прописываем заполнение переменой "АктинаяЯчейка" объявленной в модуле формы:
8) У поля ввода "СтрокаПоиска" (пункт 3), создаем обработчик события "АвтоПодбор", в нем прописываем:
Процедура СтрокаПоискаАвтоПодбор ( Элемент , Текст , ДанныеВыбора , Ожидание , СтандартнаяОбработка )
ПараметрыПоиска = ФормированиеСтруктурыПоиска ();
ПоискПодобноНаСервер ( ПараметрыПоиска );
9)В обработчиках команд форм "ВПЕРЕД" и "НАЗАД" пишем
НомерТекущегоДокумента = СтрокаДокумента [ 0 ]. СистемныеПоляНомерПоПорядку ;
Если ВыборкаПоОбъект . Количество () > НомерТекущегоДокумента Тогда
Элементы . Список . ТекущаяСтрока = ВыборкаПоОбъект [ НомерТекущегоДокумента ]. ССылка ;
Иначе
Сообщить ( "Элемент не найден, или поиск окончен" );
КонецЕсли;
НомерТекущегоДокумента = СтрокаДокумента [ 0 ]. СистемныеПоляНомерПоПорядку ;
Если НомерТекущегоДокумента - 2 >= 0 Тогда
Элементы . Список . ТекущаяСтрока = ВыборкаПоОбъект [ НомерТекущегоДокумента - 2 ]. ССылка ;
Иначе
Сообщить ( "Элемент не найден, или поиск окончен" );
КонецЕсли;
10) Пишем вспомогательные процедуры
ПараметрыПоиска =Новый Структура ;
ПараметрыПоиска . Вставить ( "Порядок" , Строка ( ЭтаФорма . Список . Порядок ));
ПараметрыПоиска . Вставить ( "Ссылка" , Элементы . Список . ТекущаяСтрока );
ПараметрыПоиска . Вставить ( "АктинаяЯчейка" , АктинаяЯчейка );
ПараметрыПоиска . Вставить ( "СтрокаПоиска" , Элементы . СтрокаПоиска . ТекстРедактирования );
ПараметрыПоиска . Вставить ( "Отображение" , Строка ( Элементы . Список . Отображение ));
Возврат ПараметрыПоиска ;
&НаСервереБезКонтекста
Функция ПоискПодобно ( ПараметрыПоиска )
Порядок = ПараметрыПоиска . Порядок ;
АктинаяЯчейка = ПараметрыПоиска . АктинаяЯчейка ;
ССылка = ПараметрыПоиска . ССылка ;
СтрокаПоиска = ПараметрыПоиска . СтрокаПоиска + "%" ;
СхемаКомпановкиДанныхР = Справочники . Номенклатура . ПолучитьМакет ( "Поиск" );
ЯчекааПоиска = "ТаблицаНоменклатуры." + АктинаяЯчейка ;
ТекстЗапроса = СхемаКомпановкиДанныхР . НаборыДанных . УпорядТаблица . Запрос ;
ТекстЗапроса = СтрЗаменить ( ТекстЗапроса , "&ПараметрУПР" , Порядок );
ТекстЗапроса = СтрЗаменить ( ТекстЗапроса , "&актинаяЯчйка" , ЯчекааПоиска );
СхемаКомпановкиДанныхР . НаборыДанных . УпорядТаблица . Запрос = ТекстЗапроса ;
КомпновщиеНастроекР =Новый КомпоновщикНастроекКомпоновкиДанных ;
КомпновщиеНастроекР . ЗагрузитьНастройки ( СхемаКомпановкиДанныхР . НастройкиПоУмолчанию );
НастройкиР = КомпновщиеНастроекР . Настройки ;
НастройкиР . ПараметрыДанных . Элементы [ 0 ]. Значение = СтрокаПоиска ;
НастройкиР . ПараметрыДанных . Элементы [ 1 ]. Значение = ССылка ;
если ПараметрыПоиска . Отображение <> "Список" тогда
// Натсройка отборов
НастройкиР . Отбор . Элементы [ 0 ]. Использование =истина;
НастройкиР . Отбор . Элементы [ 0 ]. ПравоеЗначение = ССылка . Родитель ;
КонецЕсли;
КомпоновщикМакетаР =Новый КомпоновщикМакетаКомпоновкиДанных ;
МакетКомпоновкиР = КомпоновщикМакетаР . Выполнить ( СхемаКомпановкиДанныхР , НастройкиР , , , Тип ( "ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений" ));
ПроцессорКомпоновкиДанныхР =Новый ПроцессорКомпоновкиДанных ;
ПроцессорКомпоновкиДанныхР . Инициализировать ( МакетКомпоновкиР );
ПроцессорВыводВКолецкцию =Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений ;
ВыборкаПоОбъект = Новый ТаблицаЗначений ;
ПроцессорВыводВКолецкцию . УстановитьОбъект ( ВыборкаПоОбъект );
ПроцессорВыводВКолецкцию . Вывести ( ПроцессорКомпоновкиДанныхР );
&НаСервере
Процедура ПоискПодобноНаСервер ( ПараметрыПоиска )
табл = ПоискПодобно ( ПараметрыПоиска );
Если ТипЗнч ( табл )= ТИп ( "ТаблицаЗначений" ) тогда
ЗначениеВРеквизитФормы ( табл , "ВыборкаПоОбъект" );
ЭтаФорма . СтрокаПоиска = ПараметрыПоиска . СтрокаПоиска ;
КонецЕсли;
КонецПроцедуры
Одним из неприятных моментов при работе с динамическими списками управляемого приложения является невозможность использования такой привычной для обычного приложения опции, как «поиск по строке». Предлагаю один из вариантов реализации поиска по первым буквам в динамическом списке на основе общей Формы Подбора из типовой конфигурации УНФ.
В описанном ниже примере поиск вместе с сортировкой по полю реализован для колонок Код, Артикул, Наименование.
Добавляем Реквизиты формы ПоискКод, ПоискАртикул, ПоискНаименование с типом «Строка».Вносим их в Группу Поиск на форме.
Программируем события поля ввода АвтоПодбор:
процедура Установитьпорядок ( ИмяПоля )
Если НЕ ( СписокЗапасов . Порядок . элементы . количество () = 1 и СписокЗапасов . Порядок . элементы [ 0 ]. поле = Новый ПолеКомпоновкиДанных ( ИмяПоля )) тогда
СписокЗапасов . Порядок . элементы . очистить ();
НЭлемент = СписокЗапасов . Порядок . Элементы . Добавить ( Тип ( «ЭлементПорядкаКомпоновкиДанных» ));
НЭлемент . Использование = Истина;
НЭлемент . Поле = Новый ПолеКомпоновкиДанных ( ИмяПоля );
НЭлемент . ТипУпорядочивания = НаправлениеСортировкиКомпоновкиДанных . Возр ;
НЭлемент . РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных . Обычный ;
конецЕсли;
КонецПроцедуры
&НаСервере
Процедура УстановитьПоискПоСтрокеНаСервере ( Текст , ИмяКолонки )
ПостроительЗапроса = новый построительЗапроса ;
ПостроительЗапроса . Текст = «ВЫБРАТЬ
| Номенклатура.Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| ПОДСТРОКА(Номенклатура.» + ИмяКолонки + » , 1, &Длина) = &Зн
| | Номенклатура.Ссылка.*>» ;
ПостроительЗапроса . Параметры . Вставить ( «Длина» , СтрДлина ( Текст ));
ПостроительЗапроса . Параметры . Вставить ( «Зн» , Текст );
Если Не СписокЗапасов . Параметры . элементы [ 2 ]. Значение = Справочники . Номенклатура . ПустаяСсылка () тогда
НЭлемент = ПостроительЗапроса . Отбор . Добавить ( «Ссылка» );
НЭлемент . ВидСравнения = ВидСравнения . ВИерархии ;
НЭлемент . Значение = СписокЗапасов . Параметры . элементы [ 2 ]. Значение ;
НЭлемент . Использование = Истина;
КонецЕсли;
ПостроительЗапроса . Выполнить ();
ТЗ = Построительзапроса . Результат . Выгрузить ();
Сп = новый СписокЗначений ;
Сп . ЗагрузитьЗначения ( ТЗ . ВыгрузитьКолонку ( «Ссылка» ));
СписокЗапасов . Отбор . Элементы . Очистить ();
НЭлемент = СписокЗапасов . Отбор . Элементы . добавить ( Тип ( «ЭлементОтбораКомпоновкиДанных» ));
Нэлемент . левоеЗначение = новый ПолекомпоновкиДанных ( «Номенклатура» );
НЭлемент . ВидСравнения = ВидСравненияКомпоновкиДанных . ВСписке ;
НЭлемент . ПравоеЗначение = Сп ;
НЭлемент . Использование = истина;
КонецПроцедуры
Таким образом получаем , что при наборе первых букв искомого варианта в поле «по артикулу» , например, группы поиска, происходит сортировка списка по колонке Артикул и отбор тех строк, артикул которых начинается с набранных символов.
Тут , конечно, был бы удачнее не отбор, а позиционирование на первом найденном в списке элементе . Т.е. мне бы хотелось в тексте запроса Построителя добавить «Первые 1» и дальше просто позиционироваться на найденной ссылке, но как найти позицию в динамическом списке ? Ответа на этот вопрос не нашла.
Буду рада любым предложениям по оптимизации этого варианта решения вопроса или ссылкам на другой вариант решения.
И все же в этом черном ящичке (динамическом списке) есть узкие места, которые влияют на производительность. Попробую описать пойманные места. Запросы в динамическом списке просты, с одной основной таблицей и без фактических соединений на уровне языка 1С.
Платформа воспроизведения: 1С:Предприятие 8.3 (8.3.6.2237)
Конфигурация: Управление торговлей, редакция 11.1 (11.1.9.56)
Процессор: 2х Intel Xeon X5660 2.8 ГГц
ОС:Win server 2008 R2 enterprise SP1 64x
MS SQL Server 2012. Выделено памяти 50 ГБ.
1С Сервер 64 (8.3.6.2237)
Все статистики и индексы абсолютно обновлены + полнотекстовые. В базе только один я, то есть никаких данных не добавляется и не изменяется. Никаких больше фонновых заданий не запущено.
Вступная часть:
Все начинается как обычно с маленького вопроса и как обычно перерастает в целый ряд вопросов «почему?»…
У пользователей было замечено частое подвисание ДС (динамический список). Со слов пользователя, крутятся часики и программа замирает.
Расследование:
Долго не пришлось искать, поскольку конфигурация давно уже изменена (не типовая на 100%), то кто-то постарался и установил у пользователей автоматическое обновление в 10 секунд, при этом на уровне конфигурации. Если кто не в курсе, это находится здесь:
На этом не все, часики, конечно, мы устранили. Но в процессе поиска и оптимизации долгих запросов на сервисах Гилева, попадаются опять же наши запросы и наш список, но с использованием оператора поиска LIKE «%%», то есть пользователи осуществляли поиск по части строки. А сервис регистрирует какие-то космические цифры от 10 секунд до (кто бы подумал) 600 секунд. Усомниться в сервисе нет причин, проверено запросом в процессе работы пользователей:
Я понимаю вашу терпеливость и жажду «запрос в студию», но, обещаю, мы к этому обязательно подойдем, и они еще успеют надоесть 🙂
Ну вот, почти добрались, запускаем первую трассировку по ДС. Я для себя сделал открытие, ну я в принципе понимал модель работы ДС, но никогда не видел этого на уровне трассировки. Так вот, на этом уровне к базе идет четыри похожих запроса, разница между ними только в условиях. Для проверки приложил их тексты отдельно (Query1.txt — Query4.txt). Кстати, когда открыть список на форме впервые, то запрос будет только один.
Здесь, как видим, нет никаких операторов LIKE. Собственно, это правильно, поскольку это просто обновили список без условий. А теперь представляем ту ситуацию, что была прежде, уровень автообновления 10 секунд и поиск по части строки, который работает крайне интересно. То есть мы уже приблизились к тому, что нагружает сервер СУБД. Забегая наперед, сейчас вообще думаем у части пользователей поубирать автообновления ДС, зачем нагружать сервер лишними пакетами запросов.
После экспериментов на реквизиты, по которым делали пользователи поиск, накладываем Индексировать с доп. упорядочиванием, чем-таки вырываем частичную победу в производительности:
Читайте также: