Как правильно организовать хранение истории состояний в 1С и оптимизировать получение последних данных

Программист 1С v8.3 (Управляемые формы) Управленческий учет IT и автоматизация бизнеса
← На главную

Разберем распространенную ситуацию: в конфигурации требуется отслеживать местоположение или состояние объектов (например, вагонов, оборудования, сотрудников) во времени. Начинающие разработчики нередко создают для этих целей два справочника — один для списка объектов, а второй для хранения истории их изменений, после чего связывают их запросом с группировкой по максимальной дате. Выясним, почему такой подход приводит к критическому падению производительности и как спроектировать систему по стандартам платформы 1С.

Анализ проблемы и поведение кэша СУБД

Рассмотрим типовой запрос, в котором из справочника истории сначала вычисляется максимальная дата для каждого объекта, а затем повторным соединением отбираются детальные данные:


ВЫБРАТЬ
    Список.Код КАК Номер,
    Список.ФлагРемонт КАК Ремонт,
    Список.КонтрагентАналитика КАК Аналитика,
    Список.ТипПользования КАК Пользование,
    МАКСИМУМ(Дислокация.ДатаПолучения) КАК МаксДата
ПОМЕСТИТЬ ВТ_ДислокацияАрхив
ИЗ
    Справочник.ДислокацияВагоныСписок КАК Список
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ДислокацияВагоныАрхив КАК Дислокация
        ПО Список.Код = Дислокация.Номер
СГРУППИРОВАТЬ ПО
    Список.Код,
    Список.ФлагРемонт,
    Список.КонтрагентАналитика,
    Список.ТипПользования
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Дислокация2.Номер,
    Дислокация2.Тип,
    Дислокация2.Статус,
    Дислокация2.СтанцияНазначения,
    Дислокация2.Оператор,
    Дислокация2.Расстояние КАК Раст,
    ВТ_ДислокацияАрхив.Ремонт,
    ВТ_ДислокацияАрхив.Аналитика,
    ВТ_ДислокацияАрхив.Пользование КАК Польз
ИЗ
    Справочник.ДислокацияВагоныАрхив КАК Дислокация2
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ДислокацияАрхив КАК ВТ_ДислокацияАрхив
        ПО Дислокация2.Номер = ВТ_ДислокацияАрхив.Номер
            И Дислокация2.ДатаПолучения = ВТ_ДислокацияАрхив.МаксДата

При тестировании такого запроса часто наблюдается эффект: первый запуск после добавления данных занимает длительное время (от десятков секунд до минут), а последующие повторные выполнения происходят практически мгновенно.

Проанализируем причину такого поведения. Быстрое повторное выполнение связано с кэшированием данных в оперативной памяти (как на уровне СУБД, так и на уровне платформы 1С). При первом выполнении данные считываются с диска в буферный кэш. Пока исходные таблицы не модифицируются, результат или страницы данных берутся напрямую из памяти. Очистка временных файлов пользователя на клиентском компьютере не сбрасывает кэш страниц СУБД на сервере, поэтому запрос продолжает выполняться быстро до перезапуска службы или модификации данных.

Ошибки в проектировании запроса к справочникам

Посмотрим, какие технические проблемы заложены в исходной схеме запроса:

  1. Отсутствие индексации временной таблицы. Во временной таблице ВТ_ДислокацияАрхив не указано предложение ИНДЕКСИРОВАТЬ ПО Номер, МаксДата. Во втором пакете соединение идет по двум неиндексированным полям, что вынуждает СУБД выполнять полное сканирование (Table Scan / Nested Loops) без использования индексов.
  2. Избыточные поля в группировке. В первом пакете выполняется группировка по строковым и ссылочным полям (ФлагРемонт, КонтрагентАналитика, ТипПользования). Это многократно увеличивает объем промежуточных вычислений. Достаточно было сгруппировать только ключевые поля и дату, а дополнительные реквизиты присоединять в финальном пакете.
  3. Опасность дублирования записей. Если в архиве появятся две записи по одному и тому же вагону с абсолютно одинаковым временем фиксации (ДатаПолучения), соединение ПО Номер И МаксДата вернет дубли строк в итоговой выборке.
  4. Неуникальные составные связи. Соединение объектов выполняется по числовому полю Номер, а не по системной ссылке Ссылка, что исключает эффективное использование кластерных индексов.

Каноническое решение: использование периодического регистра сведений

Хранение истории изменения состояний объектов в справочниках является грубой архитектурной ошибкой. В платформе 1С для этих целей предназначен объект метаданных Регистр сведений с установленной периодичностью (например, По позиции регистратора или В пределах секунды).

Разберем шаги по правильной организации структуры:

  1. Создадим регистр сведений (например, ДислокацияВагонов) с периодичностью По позиции регистратора или В пределах секунды.
  2. Добавим измерение Вагон (тип СправочникСсылка.ДислокацияВагоныСписок) и установим для него свойство Ведущее. Это заставит СУБД создать оптимальный индекс по сочетанию [Измерение + Период].
  3. Добавим необходимые ресурсы и реквизиты: СтанцияНазначения, Статус, Оператор, Расстояние, Тип.

Теперь получение актуального среза на любую дату выполняется в одну строку через виртуальную таблицу СрезПоследних:


ВЫБРАТЬ
    Список.Код КАК Номер,
    Список.ФлагРемонт КАК Ремонт,
    Список.КонтрагентАналитика КАК Аналитика,
    Список.ТипПользования КАК Пользование,
    Срез.Статус,
    Срез.СтанцияНазначения,
    Срез.Оператор,
    Срез.Расстояние КАК Раст,
    Срез.Тип
ИЗ
    Справочник.ДислокацияВагоныСписок КАК Список
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ДислокацияВагонов.СрезПоследних(&Период, ) КАК Срез
        ПО Список.Ссылка = Срез.Вагон

Важное правило работы с виртуальными таблицами: если требуется отобрать срез только по определенному подмножеству вагонов, условия обязательно нужно передавать в первый или второй параметр виртуальной таблицы СрезПоследних(&Период, Вагон В (&СписокВагонов)), а не в секцию ГДЕ. Это позволит платформе сразу ограничить выборку на уровне СУБД, не сканируя весь регистр.

Альтернативный шаблон: разделение истории и оперативного среза

Если система высоконагруженная, а количество записей истории исчисляется миллионами, вычисление среза последних даже по периодическому регистру может требовать ресурсов СУБД при частых обращениях. В таких сценариях применяют паттерн раздельного хранения:

  1. Периодический регистр сведений хранит полную историю всех передвижений. Запись в него производится документами или регламентными заданиями загрузки.
  2. Непериодический регистр сведений (Текущее состояние) содержит ровно одну запись на каждый вагон (Измерение: Вагон; Ресурсы: АктуальнаяДата, Станция, Статус и т.д.). При записи новой координаты в историю значение в непериодическом регистре просто перезаписывается.

Запрос к актуальным данным из непериодического регистра обращается к физической таблице без агрегатных функций МАКСИМУМ и без использования подзапросов, обеспечивая минимальное время отклика даже в файловых базах данных.

← На главную