В практике разработки на платформе 1С:Предприятие часто встречается задача быстрого отображения остатков товаров в списках или подборах. Одним из "старых" методов решения этой задачи является хранение текущих остатков непосредственно в реквизитах справочника Номенклатура. Однако такой подход часто ведет к деградации производительности системы. Разберем детально, что происходит "под капотом" платформы при перезаписи справочника, почему это может занимать много времени и какие альтернативные архитектурные решения существуют.
Многие начинающие разработчики полагают, что платформа 1С перед записью объекта сравнивает новые значения реквизитов со старыми и обновляет в СУБД только измененные поля. Это заблуждение. Проанализируем, как на самом деле работает метод Записать() на уровне СУБД:
Записать() платформа формирует SQL-запрос UPDATE. В этот запрос включаются все без исключения колонки основной таблицы справочника, независимо от того, меняли вы их или нет.Номенклатура их может быть десятки: по коду, по наименованию, по владельцу, по артикулу и любым другим индексированным реквизитам. Каждая перезапись объекта заставляет СУБД пересчитывать эти индексы, что создает колоссальную нагрузку на дисковую подсистему.Рассмотрим ситуацию, когда регламентное задание каждые 5 минут обновляет остатки. Если в справочнике 100 000 позиций, и вы перезаписываете каждую, система выполняет 100 000 тяжелых операций обновления, даже если остаток изменился всего у пары сотен товаров. Это и есть главная причина "тормозов".
Выясним причину, почему запись в регистр сведений обычно происходит быстрее, чем в справочник:
Во-первых, это ширина таблицы. Таблица справочника Номенклатура крайне "раздута". В ней хранятся текстовые описания, ссылки на единицы измерения, группы, производители и множество служебных полей. Регистр сведений (РС), предназначенный для остатков, содержит всего 2–3 измерения (номенклатура, склад) и 1–2 ресурса. Объем передаваемых данных и объем логов транзакций при записи РС в разы меньше.
Во-вторых, это бизнес-логика. При записи объекта справочника срабатывают события в модуле объекта и модуле менеджера (ПередЗаписью, ПриЗаписи). В типовых конфигурациях в этих событиях прописаны сотни проверок, заполнение дополнительных реквизитов, проверка прав доступа (RLS) и регистрация изменений для планов обмена. Все это требует времени и вычислительных ресурсов сервера приложений.
Если архитектурное решение с хранением в справочнике нельзя изменить быстро, проанализируем способ ускорения записи. Одним из самых эффективных методов является использование свойства ОбменДанными.Загрузка.
Установка этого флага в Истина сообщает платформе, что объект записывается в режиме загрузки данных из другого источника. В этом случае:
Посмотрим на пример кода, как правильно выполнять обновление остатков в справочнике:
// Пример оптимизированного обновления реквизитов справочника
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Остатки.Номенклатура КАК Ссылка,
| Остатки.КоличествоОстаток КАК НовыйОстаток
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(&Период) КАК Остатки
|ГДЕ
| Остатки.КоличествоОстаток <> Остатки.Номенклатура.ТекущийОстатокРеквизит";
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
ОбъектНоменклатура = Выборка.Ссылка.ПолучитьОбъект();
ОбъектНоменклатура.ТекущийОстатокРеквизит = Выборка.НовыйОстаток;
// Включаем режим загрузки для ускорения
ОбъектНоменклатура.ОбменДанными.Загрузка = Истина;
Попытка
ОбъектНоменклатура.Записать();
Исключение
ЗаписьЖурналаРегистрации("Ошибка обновления остатка", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
Обратите внимание: в запросе мы добавили условие ГДЕ Остатки.КоличествоОстаток <> Остатки.Номенклатура.ТекущийОстатокРеквизит. Это критически важный момент — мы будем перезаписывать только те элементы, у которых остаток действительно изменился. Это снизит нагрузку на БД в десятки раз.
Рассмотрим альтернативу — создание отдельного непериодического регистра сведений для хранения остатков. Многие опасаются, что чтение через ЛЕВОЕ СОЕДИНЕНИЕ будет медленнее, чем прямое обращение к реквизиту справочника через точку. Проанализируем это.
На самом деле, когда вы обращаетесь к реквизиту через точку в запросе (например, Номенклатура.ТекущийОстаток), платформа 1С на уровне SQL все равно выполняет LEFT JOIN с таблицей справочника. Если вы сделаете явное соединение с небольшим регистром сведений по индексированному полю Номенклатура, скорость будет практически идентичной. Более того, работа с узкой таблицей регистра часто оказывается быстрее, так как она лучше кэшируется в оперативной памяти SQL-сервера.
Преимущества использования Регистра Сведений:
В отраслевых решениях, таких как "Магазин автозапчастей", где ассортимент огромен, а остатки меняются часто, лучше использовать комбинированный подход. Вместо регламентного задания, которое "лопатит" весь справочник, можно использовать событийную модель.
Проанализируем алгоритм:
ПриЗаписи для всех документов, которые двигают остатки (Поступление, Реализация, Перемещение).Такой метод гарантирует, что система не будет делать бесполезную работу, обновляя остатки у товаров, которые не продавались годами.
Подведем итог нашему анализу. Если вы столкнулись с медленной записью справочника при обновлении остатков:
ПолучитьОбъект().Записать() только при наличии расхождений.ОбменДанными.Загрузка = Истина: Это отключит тяжелые проверки и ускорит процесс записи в несколько раз.Соблюдение этих правил позволит сделать вашу систему быстрой и отзывчивой даже при больших объемах данных и интенсивном товарообороте.