В процессе развития самописных конфигураций на платформе 1С разработчики часто сталкиваются с ситуацией, когда первоначальные архитектурные решения перестают удовлетворять требованиям производительности. Типичный пример — получение аналитических данных (оборотов продаж) напрямую из таблиц документов. Когда объем данных в базе вырастает, такие запросы начинают работать крайне медленно, а попытки «склеить» данные разных типов оплат через временные костыли только усугубляют ситуацию.
В этой статье мы разберем, почему получение данных из первички — это тупиковый путь, и как правильно реализовать консолидацию данных по продажам (эквайринг и наличные) с использованием современных механизмов платформы 1С.
Рассмотрим ситуацию: в системе есть два типа документов или два вида операций (наличные и эквайринг). Автор задачи хочет объединить их в один поток данных, убрав разрез по виду оплаты, и при этом сделать это максимально незаметно для пользователя. Первое импульсивное решение, которое часто приходит в голову — написать запрос с использованием оператора ОБЪЕДИНИТЬ ВСЕ (UNION ALL) к таблицам документов.
Проанализируем, к чему это приведет при росте базы:
Посмотрим на рекомендации опытных разработчиков: единственный верный способ решения задачи — внедрение Регистра накопления.
Вместо того чтобы пытаться создать «консолидирующий документ», который будет дублировать данные, мы создадим объект РегистрНакопления с видом «Обороты». Это позволит нам хранить данные в свернутом и оптимизированном виде.
Разберем структуру будущего регистра:
Именно использование регистра позволит платформе задействовать Виртуальные таблицы. Запрос к виртуальной таблице Обороты выполняется в десятки раз быстрее, так как платформа обращается к заранее рассчитанным итогам, а не к миллионам строк первичных документов.
Чтобы изменения в базе происходили «незаметно» для пользователя и не требовали переписывания кода каждого документа в самописной конфигурации, воспользуемся механизмом Подписок на события. Нам понадобятся подписки на событие ОбработкаПроведения для обоих типов документов продажи.
Проанализируем пример кода, который может быть размещен в модуле подписки:
Процедура ПриПроведенииПродажи(Источник, Отказ, РежимПроведения) Экспорт
// Подготавливаем набор записей нашего нового регистра
НаборЗаписей = РегистрыНакопления.ПродажиКонсолидированные.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(Источник.Ссылка);
// Заполняем движения на основании данных документа
Для Каждого СтрокаТЧ Из Источник.Товары Цикл
Движение = НаборЗаписей.Добавить();
Движение.Период = Источник.Дата;
Движение.Регистратор = Источник.Ссылка;
Движение.Номенклатура = СтрокаТЧ.Номенклатура;
Движение.Количество = СтрокаТЧ.Количество;
Движение.Сумма = СтрокаТЧ.Сумма;
КонецЦикла;
// Записываем движения
НаборЗаписей.Записать();
КонецПроцедуры
Использование свойства ОбменДанными.Загрузка = Истина при программной записи может потребоваться только в специфических случаях, но в стандартном процессе проведения мы просто формируем движения по новому регистру. Это гарантирует, что при отмене проведения документа данные из консолидированного регистра также «уйдут» автоматически.
Поскольку задача подразумевает работу с уже существующими данными «задним числом», нам нужно заполнить новый регистр за прошлые периоды. Делать это простым перепроведением всех документов опасно — это вызовет длительные блокировки базы.
Разберем алгоритм безопасного заполнения:
Посмотрим, как эффективно работать с набором записей без лишних проверок:
// Пример порционного заполнения
Запрос = Новый Запрос("ВЫБРАТЬ Ссылка ИЗ Документ.ПродажаНаличные ГДЕ Дата МЕЖДУ &Начало И &Конец");
// ... установка параметров ...
Результат = Запрос.Выполнить().Выбрать();
Пока Результат.Следующий() Цикл
Набор = РегистрыНакопления.ПродажиКонсолидированные.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(Результат.Ссылка);
// Логика заполнения набора записей
// ...
Набор.Записать();
КонецЦикла;
Автор темы изначально рассматривал вариант создания некоего общего документа. Выясним, почему коллеги на форуме категорически против этого:
Регистр накопления лишен этих недостатков. Он является подчиненным объектом (движением) по отношению к документу-регистратору. Платформа 1С берет на себя всю работу по контролю целостности: если документ удаляется, его движения по регистру стираются автоматически.
Подведем итог нашему анализу. Чтобы объединить продажи по разным видам оплат и обеспечить быструю работу системы при росте базы, следует придерживаться следующего плана:
Помните, что архитектурная чистота системы — это не просто «красивый код», а залог того, что через год при удвоении объема операций ваши отчеты не будут формироваться по несколько минут, вызывая недовольство пользователей и руководства.