В статье показан пример оптимизации запроса к информационной базе, позволивший ускорить вывод информации для пользователей.
Задача решалась в нестандартной конфигурации, поэтому такая особенность платформы, как RLS, с помощью которой данную задачу можно решить в стандартных продуктах, не применялась. Описанный ниже пример пригоден для решения таких задач, как вывод в формах списков нестандартных вычисляемых реквизитов. В практике они встречаются довольно часто.
Описание задачи.
В табличной части(далее ТЧ) ПраваДоступа справочника Контрагенты есть Сотрудники, работающие в разных Отделах. В зависимости от Отделов задаются Приоритеты, используя которые нужно выбрать одного из пользователей, который отличается от другого реквизита справочника – Ответственный - и добавить его к основной таблице справочника Контрагенты.
Не очень тривиальная задача, которая ранее решалась запросом к ТЧ в цикле:
ВЫБРАТЬ ПЕРВЫЕ 1
КонтрагентыПраваДоступа.Сотрудник,
КонтрагентыПраваДоступа.Сотрудник. Подразделение,
ВЫБОР
КОГДА КонтрагентыПраваДоступа.Сотрудник. Подразделение = ЗНАЧЕНИЕ(Справочник.Подразделения.ПодразделениеНомерОдин)
ТОГДА 1…
2…4
КОНЕЦ КАК Приоритет
ИЗ
Справочник.Контрагенты.ПраваДоступа КАК ОрганизацииПраваДоступа
ГДЕ
КонтрагентыПраваДоступа .Сотрудник <> &ТекущийПользователь
И КонтрагентыПраваДоступа.Ссылка = &Контрагент
УПОРЯДОЧИТЬ ПО
Приоритет
Запрос в цикле это всегда плохо. В данной статье я хочу показать метод решения этой задачи в рамках одного запроса.
Чтобы продемонстрировать процесс возьмём абстрактный контрольный пример, по которому будем последовательно получать требуемый результат.
Пусть имеются 6 элементов справочника Контрагенты К1…К6, у которых ТЧ Права Доступа заполнены следующим образом:
Контрагент |
Сотрудник |
Подразделение |
К1 |
С1 |
П1 |
К1 |
С2 |
П2 |
К2 |
С3 |
П3 |
К3 |
С4 |
П4 |
К3 |
С8 |
П4 |
К4 |
С0 |
П1 |
К5 |
С0 |
П1 |
К6 |
С5 |
П2 |
К6 |
С6 |
П3 |
К6 |
С7 |
П2 |
К6 |
С0 |
П1 |
где С0 – текущий пользователь, П1…4 – подразделения. Для простоты номер подразделения = приоритет вывода.
Эта таблица выбрана из справочника Контагенты по условию равенства значения реквизита Ответственный заданному пользователю - С0.
Для решения воспользуемся таким инструментом, как временные таблицы. Для начала выберем из исходной ТЧ тех контрагентов, у которых есть пользователи, отличные от текущего, чтобы реализовать условие
КонтрагентыПраваДоступа. Сотрудник <> &ТекущийПользователь (С0).
Контрагент |
Подразделение |
К1 |
П1 |
К1 |
П2 |
К2 |
П3 |
К3 |
П4 |
К3 |
П4 |
К6 |
П2 |
К6 |
П3 |
К6 |
П2 |
К6 |
П1 |
Во временной таблице КонтрагентыСДругимиСотрудниками останутся К1, К2, К3, К6. Добавив к этой таблице поле
Приоритет, с помощью оператора ВЫБОР, получим следующую таблицу:
Контрагент |
Подразделение |
Приоритет |
К1 |
П1 |
1 |
К1 |
П2 |
2 |
К2 |
П3 |
3 |
К3 |
П4 |
4 |
К3 |
П4 |
4 |
К6 |
П2 |
2 |
К6 |
П3 |
3 |
К6 |
П2 |
2 |
Применим группировку по полю Контрагент, чтобы выбрать минимальное значение поля Приоритет:
Контрагент |
Подразделение |
Приоритет |
К1 |
П1 |
1 |
К2 |
П3 |
3 |
К3 |
П4 |
4 |
К6 |
П2 |
2 |
Запишем эту таблицу во временную – Приоритеты. По условиям задачи в Правах доступа могут быть несколько сотрудников одного подразделения. Поэтому таблицы Приоритеты недостаточно. Нужно понимать принцип, по которому из нескольких сотрудников будет выбираться один. В моём случае было решено использовать ссылку на сотрудника. Вообще, это может быть и любой другой критерий. Например, алфавитный порядок сотрудников или ещё что-то. Важно, что для его использования понадобится ещё одна временная таблица, созданная на основе второй, а в первой понадобится ещё одно поле, получаемое из исходной таблицы:
Контрагент |
Подразделение |
Приоритет |
Сотрудник |
К1 |
П1 |
1 |
С1 |
К1 |
П2 |
2 |
С2 |
К2 |
П3 |
3 |
С3 |
К3 |
П4 |
4 |
С4 |
К3 |
П4 |
4 |
С8 |
К6 |
П2 |
2 |
С5 |
К6 |
П3 |
3 |
С6 |
К6 |
П2 |
2 |
С7 |
Для получения нужного результат свяжем временные таблицы Приоритеты и КонтрагентыСДругимиСотрудниками по условиям совпадения полей Контрагент и Приоритет:
Контрагент |
Подразделение |
Приоритет |
Сотрудник |
К1 |
П1 |
1 |
С1 |
К2 |
П3 |
3 |
С3 |
К3 |
П4 |
4 |
С4 |
К3 |
П4 |
4 |
С8 |
К6 |
П2 |
2 |
С5 |
К6 |
П2 |
2 |
С7 |
И, наконец, группировкой по остальным полям выберем минимальные значения по полю Сотрудник и запишем результат во временную таблицу Сотрудники:
Контагент |
Подразделение |
Приоритет |
Сотрудник |
К1 |
П1 |
1 |
С1 |
К2 |
П3 |
3 |
С3 |
К3 |
П4 |
4 |
С4 |
К6 |
П2 |
2 |
С5 |
Эта таблица однозначно определяет единственного сотрудника для каждого контрагента.
Осталось решить задачу добавления значений в поле таблицы справочника, что достигается левым соединением исходной таблицы с последней таблицей Сотрудники. Для корректности требуется выбрать значение заполнения для тех контрагентов, у которых в ТЧ нет других сотрудников. Это К4 и К5. Значением может быть NULL, Неопределено или ПустаяСсылка справочника Сотрудники, в зависимости от дальнейшего использования полученной таблицы. Как правило, для формы списка справочника. Значения заполняются функцией языка запросов ЕСТЬNULL:
Организация |
Сотрудник |
К1 |
С1 |
К2 |
С3 |
К3 |
С4 |
К4 |
NULL |
К5 |
NULL |
К6 |
С5 |
Все действия выполняются в пределах одного запроса, что даёт существенное сокращение времени его выполнения по сравнению с запросом в цикле. Так, для контрольного примера из 1312 записей справочника время выполнения кода в начальном варианте, с запросом в цикле, составило 3,3507 сек, в изменённом, с временными таблицами – 0,5589 сек.
Целью статьи было продемонстрировать на примере решения реальной задачи, что средства платформы 1С позволяют реализовать достаточно сложные алгоритмы по выборке данных в рамках одного запроса. Описанный выше пример работает без проблем для обычных форм. Что касается управляемых, то нужно учесть, что динамические списки не дружат с временными таблицами из-за того, что оба объекта формируются в оперативной памяти. Но это уже другая тема - можно использовать вложенные запросы, либо предварительно формировать запросом с временными таблицами таблицу значений и строить динамический список по ней. Хотя, начиная с версии платформы 8.3.8. в динамическом списке и реализована поддержка работы с пакетным запросом, но общие рекомендации 1С сводятся к тому, что запросы динамических списков должны быть максимально простыми и от динамических списков стоит отказаться при выводе не столь значимой информации, получение которой приводит к усложнению запроса.
Значимость информации - субъективный момент, зависящий от постановки задачи. В общем случае, что кажется руководителю важным, то и нужно выводить, стараясь, при этом, избегать запросов в цикле, замедляющих работу системы.
ВЫБРАТЬ РАЗЛИЧНЫЕ
КонтрагентыПраваДоступа.Ссылка.Ответственный КАК Ответственный,
КонтрагентыПраваДоступа.Ссылка,
ПОМЕСТИТЬ КонтрагентыОтветственного
ИЗ
Справочник.Контрагенты.ПраваДоступа КАК КонтрагентыПраваДоступа
ГДЕ
КонтрагентыПраваДоступа.Ссылка.Ответственный = &ПользовательТекущий
И НЕ КонтрагентыПраваДоступа.Ссылка.ПометкаУдаления
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
КонтрагентыПраваДоступа.Ссылка,
КонтрагентыПраваДоступа.Сотрудник.Подразделение КАК Подразделение,
ВЫБОР
КОГДА КонтрагентыПраваДоступа.Сотрудник.Подразделение = ЗНАЧЕНИЕ(Справочник.Подразделения.П1)
ТОГДА 1
КОГДА КонтрагентыПраваДоступа.Сотрудник.Подразделение = ЗНАЧЕНИЕ(Справочник.Подразделения.П2)
ТОГДА 2
КОГДА КонтрагентыПраваДоступа.Сотрудник.Подразделение = ЗНАЧЕНИЕ(Справочник.Подразделения.П3)
ТОГДА 3
ИНАЧЕ 4
КОНЕЦ КАК Приоритет,
КонтрагентыПраваДоступа.Сотрудник КАК Сотрудник,
КонтрагентыПраваДоступа.Сотрудник.Подразделение КАК Подразделение
ПОМЕСТИТЬ КонтрагентыСДругимиСотрудниками
ИЗ
КонтрагентыОтветственного КАК КонтрагентыОтветственного
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты.ПраваДоступа КАК КонтрагентыПраваДоступа
ПО КонтрагентыОтветственного.Ссылка = КонтрагентыПраваДоступа.Ссылка
ГДЕ
КонтрагентыПраваДоступа.Пользователь <> &ПользовательТекущий
И НЕ КонтрагентыПраваДоступа.Пользователь.ПометкаУдаления
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
КонтрагентыСДругимиСотрудниками.Ссылка,
МИНИМУМ(КонтрагентыСДругимиСотрудниками.Приоритет) КАК Приоритет,
МИНИМУМ(КонтрагентыСДругимиСотрудниками.Подразделение) КАК Подразделение,
КонтрагентыСДругимиСотрудниками.Ссылка
ПОМЕСТИТЬ Приоритеты
ИЗ
КонтрагентыСДругимиСотрудниками КАК КонтрагентыСДругимиСотрудниками
СГРУППИРОВАТЬ ПО
КонтрагентыСДругимиСотрудниками.Ссылка
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Приоритеты.Ссылка,
Приоритеты.Приоритет,
Приоритеты.Подразделение,
МИНИМУМ(КонтрагентыСДругимиСотрудниками.Сотрудник) КАК Сотрудник
ПОМЕСТИТЬ Сотрудники
ИЗ
Приоритеты КАК Приоритеты
ЛЕВОЕ СОЕДИНЕНИЕ КонтрагентыСДругимиСотрудниками КАК КонтрагентыСДругимиСотрудниками
ПО Приоритеты.Ссылка = КонтрагентыСДругимиСотрудниками.Ссылка
И Приоритеты.Приоритет = КонтрагентыСДругимиСотрудниками.Приоритет
СГРУППИРОВАТЬ ПО
Приоритеты.Ссылка,
Приоритеты.Приоритет,
Приоритеты.Подразделение
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
КонтрагентыОтветственного.Ссылка,
ЕСТЬNULL(КонтрагентыСДругимиСотрудниками.Пользователь, НЕОПРЕДЕЛЕНО) КАК Сотрудник
ИЗ
КонтрагентыОтветственного КАК КонтрагентыОтветственного
ЛЕВОЕ СОЕДИНЕНИЕ Сотрудники КАК Сотрудники
ЛЕВОЕ СОЕДИНЕНИЕ КонтрагентыСДругимиСотрудниками КАК КонтрагентыСДругимиСотрудниками
ПО Сотрудники.Ссылка = КонтрагентыСДругимиСотрудниками.Ссылка
И Сотрудники.Сотрудник = КонтрагентыСДругимиСотрудниками.Сотрудники
ПО КонтрагентыОтветственного.Ссылка = Сотрудники.Ссылка