Перевод базы данных 1С с файлового варианта на клиент-серверный (MS SQL Server или PostgreSQL) часто преподносит неприятные сюрпризы в виде резкого падения производительности. Одной из классических проблем является работа динамических списков (или табличных полей с типом ДокументСписок) в обычных формах, когда в них накладываются программные отборы по большому количеству ссылок. Проанализируем, почему это происходит, и как правильно перестроить механизмы работы программы.
В файловом варианте 1С обрабатывает данные во внутренней памяти, и фильтрация по списку значений (даже если в нем сотни элементов) происходит относительно быстро. Однако в клиент-серверном варианте любое условие в отборе превращается в часть SQL-запроса WHERE.
Когда мы используем ВидСравнения.НеВСписке или ВидСравнения.ВСписке, 1С передает SQL-серверу массив уникальных идентификаторов (GUID). Если список содержит тысячи документов, SQL-сервер сталкивается с рядом трудностей:
NOT IN (соответствует НеВСписке) практически всегда заставляет СУБД выполнять полное сканирование таблицы (Index Scan), вместо быстрого поиска (Index Seek). Это и приводит к тем самым «тормозам», которые заметил автор темы.Если архитектура не позволяет быстро внести изменения в структуру метаданных, можно попробовать оптимизировать сам запрос, который собирает список ссылок для отбора. Рассмотрим исходный вариант кода из сообщения пользователя:
Запрос = новый запрос;
Запрос.Текст = "ВЫБРАТЬ
| "+ВидДок+"Товары.Ссылка
|ИЗ
| Документ."+ВидДок+".Товары КАК "+ВидДок+"Товары
|
|СГРУППИРОВАТЬ ПО
| "+ВидДок+"Товары.Ссылка
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| "+ВидДок+"Оборудование.Ссылка
|ИЗ
| Документ."+ВидДок+".Оборудование КАК "+ВидДок+"Оборудование
|
|СГРУППИРОВАТЬ ПО
| "+ВидДок+"Оборудование.Ссылка";
Тз = Запрос.Выполнить.Выгрузить();
Проанализируем ситуацию: использование СГРУППИРОВАТЬ ПО внутри объединения может быть избыточным. Для SQL-сервера эффективнее использовать ВЫБРАТЬ РАЗЛИЧНЫЕ (DISTINCT) или собрать ссылки в одну временную таблицу и уже из нее получить уникальные значения.
Также, как показала практика (сообщение 17), переход от логики «Показать всё, кроме документов с товарами» (НеВСписке) к логике «Показать только документы с услугами» (ВСписке) может значительно ускорить работу, если количество документов во втором случае меньше.
Самый правильный и «чистый» способ решения проблемы — уход от программных отборов по ссылкам в пользу отбора по конкретному реквизиту объекта. Разберем по шагам, как это реализовать:
ПоступлениеТоваровУслуг создаем новый реквизит ТолькоУслуги с типом Булево.Товары и Оборудование и установит флаг ТолькоУслуги = Истина там, где товаров нет.ПередЗаписью добавьте логику автоматического обновления этого флага при изменении состава табличных частей.После этого отбор в форме списка станет максимально простым и быстрым:
ДокументСписок.Отбор.ТолькоУслуги.ВидСравнения = ВидСравнения.Равно;
ДокументСписок.Отбор.ТолькоУслуги.Значение = Истина;
ДокументСписок.Отбор.ТолькоУслуги.Использование = Истина;
Такой запрос SQL-сервер выполнит мгновенно, используя индекс по созданному полю.
При использовании программного списка (Решение 1) часто возникает ошибка: «Введенные данные не отображены в списке, так как не соответствуют отбору». Это происходит потому, что в момент записи нового документа он еще не включен в ваш массив СписокД, сформированный при открытии формы.
Чтобы решить эту проблему в обычных формах, выполним следующие действия:
ПослеЗаписи добавим оповещение системы:
Оповестить("ОбновитьСписокПоступлений");
ОбработкаОповещения перехватим это событие:
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
Если ИмяСобытия = "ОбновитьСписокПоступлений" Тогда
// Повторно вызываем получение списка документов и устанавливаем отбор
СписокД = РаботаСДиалогами.ПолучитьСписокДоков("ПоступлениеТоваровУслуг");
ДокументСписок.Отбор.Ссылка.Значение = СписокД;
КонецЕсли;
КонецПроцедуры
Важное замечание: Если вы используете Решение 2 (индексированный реквизит), эта проблема исчезнет сама собой, так как документ сразу будет иметь признак ТолькоУслуги = Истина, и динамический список подхватит его автоматически без дополнительных манипуляций с кодом.
Проанализировав ситуацию, можно сделать вывод: клиент-серверная архитектура не прощает «ленивых» решений, которые работали в файловом режиме. Основные правила для высокопроизводительных форм списка в 1С на SQL:
ВидСравнения.НеВСписке.ВЫБРАТЬ РАЗЛИЧНЫЕ вместо тяжелых группировок, если вам нужны только уникальные ссылки.Оповестить и ОбработкаОповещения, но делайте это аккуратно, чтобы не вызвать лишних перезапросов к базе данных при каждом действии пользователя.