При работе с высоконагруженными системами на базе 1С:Предприятие 8, где количество записей в регистрах накопления исчисляется миллионами и десятками миллионов, перед разработчиком неизбежно встает вопрос: использовать ли стандартные виртуальные таблицы (Остатки, Обороты, ОстаткиИОбороты) или писать запросы напрямую к физической таблице движений? Существует мнение, что «прямой» запрос к физике может быть быстрее, однако на практике всё гораздо сложнее. В этой статье мы подробно разберем внутренние механизмы платформы, проанализируем разницу в поведении СУБД и выясним, как правильно строить запросы к большим данным.
Для начала разберем, что происходит «под капотом», когда мы обращаемся к виртуальной таблице. Платформа 1С не хранит данные виртуальных таблиц в готовом виде в базе данных (за исключением текущих итогов). Вместо этого она динамически формирует SQL-запрос, комбинируя данные из нескольких физических таблиц. Рассмотрим этот процесс на примере регистра остатков.
Физически в СУБД для регистра остатков существуют:
Когда мы запрашиваем виртуальную таблицу Остатки на определенную дату, система выполняет следующий алгоритм:
Выясним причину, почему это эффективно: если в регистре накоплены данные за 5 лет, нам не нужно суммировать миллионы записей с самого начала ведения учета. Платформа берет «точку опоры» в таблице итогов и добирает лишь малую часть данных из таблицы движений. Если же мы обратимся к физической таблице напрямую и попробуем посчитать остаток через СУММА(СуммаОстаток), нам придется самостоятельно описывать всю логику агрегации, что крайне трудозатратно и часто ведет к ошибкам.
Проанализируем ситуацию, описанную многими разработчиками: почему на PostgreSQL запросы к виртуальным таблицам иногда работают медленнее, чем на MSSQL? Причина кроется в особенностях оптимизаторов запросов этих СУБД.
Платформа 1С транслирует обращение к виртуальной таблице в сложный SQL-запрос с вложенными подзапросами (Subqueries). Оптимизатор MSSQL обычно успешно «проглатывает» такие конструкции, эффективно пробрасывая фильтры по измерениям (например, по Номенклатуре или Складу) внутрь подзапроса. Это позволяет СУБД сканировать только нужные фрагменты индексов.
PostgreSQL в некоторых случаях воспринимает вложенный запрос как «черный ящик». Вместо того чтобы сначала отфильтровать данные по конкретной номенклатуре, он может попытаться рассчитать итоги по всему регистру в памяти, а уже потом наложить фильтр. На многомиллионном регистре это приводит к колоссальным задержкам и деградации производительности.
Чтобы избежать проблем с производительностью (особенно на PostgreSQL), разберем стандартный метод, который часто называют «фэн-шуй в 1С». Суть его заключается в изоляции виртуальной таблицы от остальной логики запроса.
Рассмотрим пошагово правильный алгоритм построения сложного запроса:
Посмотрим на пример кода, иллюстрирующий этот подход:
// Шаг 1: Получаем данные из виртуальной таблицы во временную
Запрос.Текст =
"ВЫБРАТЬ
| РегистрОстатки.Номенклатура КАК Номенклатура,
| РегистрОстатки.КоличествоОстаток КАК Остаток
|ПОМЕСТИТЬ ВТ_Остатки
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, Номенклатура В (&МассивНоменклатуры)) КАК РегистрОстатки
|ИНДЕКСИРОВАТЬ ПО
| Номенклатура";
// Шаг 2: Работаем с подготовленными данными
Запрос.Текст = Запрос.Текст +
"ВЫБРАТЬ
| ВТ_Остатки.Номенклатура,
| ВТ_Остатки.Остаток,
| Цены.Цена
|ИЗ
| ВТ_Остатки КАК ВТ_Остатки
|ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ) КАК Цены
|ПО ВТ_Остатки.Номенклатура = Цены.Номенклатура";
Такой подход заставляет СУБД сначала выполнить максимально эффективную выборку из итогов, а затем работать с компактным набором данных в памяти.
Несмотря на мощь виртуальных таблиц, бывают исключения. Разберем ситуации, когда обращение к физической таблице (РегистрНакопления.ИмяРегистра) оправдано:
Остатки) отсутствуют Реквизиты движений. Если вам нужно получить значение, которое не является измерением или ресурсом, придется идти в физическую таблицу.Важный нюанс: при работе с физической таблицей разработчики часто забывают про поле Активность (_Active в терминах СУБД). Виртуальные таблицы учитывают его автоматически, отсекая сторнированные или помеченные на удаление движения. В физической таблице фильтр ГДЕ Активность = ИСТИНА нужно накладывать вручную.
Проанализируем ситуацию с регистрами оборотов. В отличие от остатков, для оборотных регистров использование итогов является опциональным (их можно включить или выключить в конфигураторе).
Если итоги оборотов включены, виртуальная таблица Обороты работает аналогично остаткам — использует агрегаты за месяц. Если итоги выключены, виртуальная таблица превращается в обычный SELECT SUM(...) из физической таблицы. В этом случае разницы в производительности между ВТ и ФТ практически нет, но использование ВТ все равно предпочтительнее с точки зрения чистоты кода и автоматической фильтрации по активности записей.
Стоит отметить, что в последних релизах платформы механизмы работы с итогами были значительно оптимизированы. Платформа стала «умнее» анализировать параметры виртуальных таблиц. Например, если запрос идет строго на границу месяца, 1С может полностью исключить обращение к таблице движений, прочитав данные только из таблицы итогов.
Также не забывайте про режим Разделения итогов. На многомиллионных регистрах, куда идет интенсивная запись, этот механизм позволяет избежать блокировок таблицы итогов. Хотя это больше влияет на скорость записи, косвенно это ускоряет и чтение, так как уменьшается время ожидания освобождения ресурсов СУБД.
Подводя черту под анализом использования таблиц в регистрах накопления, можно сформулировать следующие рекомендации:
ГДЕ.Соблюдение этих правил позволит вашей системе сохранять высокую отзывчивость даже при накоплении десятков миллионов записей в регистрах.