В процессе поддержки старых конфигураций на платформе 1С:Предприятие 7.7 программисты часто сталкиваются с кодом, написанным много лет назад. Одной из типичных проблем является неоптимальный поиск данных в справочниках, например, получение индивидуальных настроек пользователя (скидок, прав доступа, наценок) путем перебора всех элементов в цикле. В этой статье мы проанализируем, как ускорить такие операции, разберем плюсы и минусы кэширования в глобальные переменные и узнаем секрет принудительного обновления данных из базы.
Рассмотрим ситуацию: в системе есть справочник УправлениеДатамиРедактирования (или аналогичный), где хранятся параметры для каждого пользователя. Изначально код может выглядеть так:
СпрДат = СоздатьОбъект("Справочник.УправлениеДатамиРедактирования");
СпрДат.ВыбратьЭлементы();
Пока СпрДат.ПолучитьЭлемент() = 1 Цикл
Если СпрДат.Наимен.Наименование = ИмяПользователя() Тогда
МаксСкидка = СпрДат.МаксСкидка;
МинНаценка = СпрДат.МинНаценка;
КонецЕсли;
КонецЦикла;
Такой подход крайне неэффективен, так как при каждом обращении к параметрам система считывает весь справочник целиком. Если в справочнике сотни или тысячи записей, а проверка происходит в формах документов или при проведении, это создает значительную нагрузку на дисковую подсистему и сеть.
Первым шагом к оптимизации является использование встроенных механизмов поиска. Вместо цикла Пока...Цикл следует применять метод НайтиПоРеквизиту(). Однако важно помнить: этот метод будет работать быстро только в том случае, если для реквизита, по которому идет поиск, в конфигураторе установлен флаг "Сортировка" (индексация).
Проанализируем более грамотный вариант поиска, предложенный участниками обсуждения:
СпрПользователи = СоздатьОбъект("Справочник.Пользователи");
Если СпрПользователи.НайтиПоНаименованию(ИмяПользователя()) = 1 Тогда
СпрДат = СоздатьОбъект("Справочник.УправлениеДатамиРедактирования");
// Подразумеваем, что "Наимен" — это реквизит типа Справочник.Пользователи
Если СпрДат.НайтиПоРеквизиту("Наимен", СпрПользователи.ТекущийЭлемент(), 1) = 1 Тогда
МаксСкидка = СпрДат.МаксСкидка;
МинНаценка = СпрДат.МинНаценка;
КонецЕсли;
КонецЕсли;
Этот метод работает значительно быстрее перебора, но все же требует обращения к базе данных при каждом вызове. Если параметры пользователя нужны "сотню раз в день", стоит задуматься о кэшировании.
Идея заключается в том, чтобы один раз при старте системы (в процедуре ПриНачалеРаботыСистемы глобального модуля) найти нужный элемент справочника и сохранить его в глобальную переменную. Тогда в любом месте конфигурации мы сможем мгновенно получить доступ к значению.
Алгоритм действий:
Перем глНастройкиПользователя Экспорт;.ПриНачалеРаботыСистемы находим элемент справочника и записываем ссылку: глНастройкиПользователя = СпрДат.ТекущийЭлемент();.ТекСкидка = глНастройкиПользователя.МаксСкидка;.Однако здесь кроется серьезная ловушка платформы 7.7, связанная с обновлением данных.
Выясним важную деталь: если администратор изменит значение реквизита в справочнике, пока пользователь работает в программе, значение в глобальной переменной глНастройкиПользователя.МаксСкидка не изменится. Платформа кэширует объект в памяти в момент получения ссылки.
Как показывают тесты на форуме, существует разница в получении данных:
Сообщить(глНастройкиПользователя.МаксСкидка); — выдает старое (закэшированное) значение.Сообщить(глНастройкиПользователя.ТекущийЭлемент().МаксСкидка); — принудительно обращается к базе данных и выдает актуальное значение.Использование конструкции Объект.ТекущийЭлемент().Реквизит позволяет обойти локальный кэш приложения. Это работает быстрее, чем полноценный поиск через НайтиПоРеквизиту(), так как система уже знает внутренний ID записи и обращается к ней напрямую.
Если для параметров (например, скидок) критически важно отслеживать изменения во времени или гарантировать получение самого свежего значения, имеет смысл сделать такие реквизиты периодическими. В 1С 7.7 периодические реквизиты всегда считываются из отдельной таблицы 1SCONST.
При использовании метода Получить(РабочаяДата()) система всегда вернет актуальное значение, игнорируя кэш объекта справочника. Но стоит учитывать, что большое количество периодических реквизитов может замедлить работу системы из-за роста таблицы констант.
Подведем итог и выберем оптимальную стратегию в зависимости от ваших задач:
ТекущийЭлемент(). Например: глНастройки.ТекущийЭлемент().Скидка.Рассмотренные подходы позволяют значительно сократить количество операций чтения из базы данных и сделать интерфейс программы более отзывчивым для конечного пользователя, при этом сохраняя надежность получения актуальных данных.