Тонкости настройки Истории данных

dbo._DataHistoryQueue0 — что это такое и как с этим бороться.

Добрый день!

Про настройки хранения истории данных написано много. Но мало информации о том, как же это все работает. Итак, при включении режима использования истории данных,

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

ИсторияДанных.ОбновитьИсторию();

Данная команда выполняется только с правами Администратор.

Если необходимо для конкретного объекта создать версию данных принудительно, то необходимо выполнить после записи объекта код:

Данные=<Ссылка на объект>.ПолучитьОбъект();

Пользователь=ПользователиИнформационнойБазы.ТекущийПользователь();

ИсторияДанных.ЗаписатьВерсию(Данные,ТекущаяДата(),Пользователь.УникальныйИдентификатор,Пользователь.Имя,Пользователь.ПолноеИмя,ВидИзмененияДанных.Изменение);

Так как же это все таки работает???

Все версии данных, при сохранении объектов попадают в очередь (таблица dbo._DataHistoryQueue0 на сервере SQL), и накапливаются там, пока не выполнится команда

ИсторияДанных.ОбновитьИсторию();

Далее записи перемещаются в dbo._DataHistoryVersions, собственно из этой таблицы мы и видим данные, когда заходим в клиенте в раздел «История изменений»

Вроде бы ничего сложного, можно пользоваться.

Оказывается, есть нюансы:

Задача:

А) Необходимо вести историю данных, причем сохранять изменения, которые делали пользователи, а не регламентные задания.

Б) Так же необходимо учесть, если объект пересоздается в другом месте БД, необходимо перенести и его историю. Например: оборудование демонтировали. В БД есть два объекта 1) Оборудование подразделения и 2) Демонтированное оборудование подразделения. При демонтаже в первом объекте запись удаляется, а во-втором создается путем копирования части реквизитов (структуры объектов не одинаковые).

Решение:Для решения пункта А) нам необходимо в объектах использовать процедуру

Процедура ПослеЗаписи(ПараметрыЗаписи)

Данные=<Ссылка на объект>.ПолучитьОбъект();

Пользователь=ПользователиИнформационнойБазы.ТекущийПользователь();

ИсторияДанных.ЗаписатьВерсию(Данные,ТекущаяДата(),Пользователь.УникальныйИдентификатор,Пользователь.Имя,Пользователь.ПолноеИмя,ВидИзмененияДанных.Изменение);

КонецПроцедуры

, где Ссылка на объект – ссылка на объект, для которого необходимо записать версию, т.е. объект, которые мы только что записали.

            Почему после записи?

            Потому что версия формируется не из формы, а из сохраненной записи БД.

            Почему не используется

ИсторияДанных.ОбновитьИсторию();

?

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

ИсторияДанных.ОбновитьИсторию(); 

все эти версии будут привязаны к объекту.

            Для решения Б) нам придется немного схитрить. Дело в том, что готовой функции переноса истории с объекта в объект нет. Так что будем переписывать историю

            После записи во-второй объект («Демонтированное оборудование подразделения»), нам необходимо его получить и добавить историю, которая была у объекта Источника:

ДанныеИсточника=<Ссылка на объект источник>;

Демонтаж.Записать();

Объект= ДанныеИсточника.ПолучитьОбъект();

Отбор = Новый Структура;

Отбор.Вставить("Данные", Объект.Ссылка);

Версии = ИсторияДанных.ВыбратьВерсии(Отбор);

ДанныеПриемника=Справочники.ДемонтированноеОборудование.НайтиПоКоду(Демонтаж.Код).ПолучитьОбъект();

Пользователь=ПользователиИнформационнойБазы.ТекущийПользователь();

Для Каждого Строка Из Версии Цикл

Строка.Данные= ДанныеПриемника;

ИсторияДанных.ЗаписатьВерсию(Строка.Данные,Строка.Дата,Строка.Пользователь,Строка.ИмяПользователя,Строка.ПолноеИмяПользователя,Строка.ВидИзмененияДанных,Строка.Комментарий,Строка.Транзакция,Строка.Узел);

КонецЦикла;

, где Ссылка на объект – ссылка на объект источника, историю которого мы хотим передать новому объекту.

ВАЖНО!!!

После переноса истории данных, во-втором объекте будут видны только те поля истории, которые совпадают по наименованию и типу с первым объектом.

Поля, которые нужны только для отображения истории первого объекта, и не используются во-втором объекте можно скрыть от пользователей и не заполнять при переносе из первого объекта.

Вроде бы задача решена, но что нам делать с очередью ИсторииДанных, которая продолжает расти, увеличивая нашу базу не по дням, а по часам.

К сожалению, во встроенной функции  ИсторияДанных такой команды нет, поэтому пришлось делать костыльное решение. В SQL создал плановое задание из двух шагов:

Use <имя БД>
truncate table dbo._DataHistoryQueue0

USE [<имя БД>]
GO

DBCC SHRINKDATABASE(N'<имя БД>' )

GO

 

Первый шаг, чистить таблицу очереди истории данных

Второй шаг, сжимает БД.

Если второй шаг не выполнять, БД не уменьшится в размерах.

В итоге размер БД сократился в 20 раз. Именно столько накопилось в очереди за 2 месяца использования истории данных.

5 Comments

  1. Darklight

    Боже мой, что же у Вас всё так сумбурно написано 🙁 Вот так и не смог понять — ради чего это всё тут написано — что за такие сложности при работе с историей?

    Я, просто, не использую типовой механизм, у меня очень давно используется самописное универсальное решение — и я просто не могу понять — откуда проблемы?

    У самописки — было только два «тонких места» — это работа в РИБ (особенно когда возникло желание существенно «упаковать» данные истории — чтобы не использовать ссылки UID по 32 байта и не дублировать одинаковые строки); и вынос истории в отдельную базу (ибо хранить её — в той же базе — это бред «сивой кобылы», такой же, нет, ещё больший, как и бред хранить в той же базе присоединённые файлы — когда они есть почти у всех первичных документов в виде куче сканов).

    А у Вас тут прям какие-то совершенно непонятные проблемы!

    Ну ясен пень — что необходимость вызывать «ИсторияДанных.ОбновитьИсторию()» в основном это бред — историю придётся писать вручную, по старинке, через подписки на события через «ИсторияДанных.ЗаписатьВерсию(…)» — ибо, если кто-то сейчас пишет историю, то наверняка кто-то может её сейчас или через 5 минут уже анализировать (часто бывает — записываешь — и сразу смотришь — что там изменилось).

    А переходить на отложенное выполонение через «ИсторияДанных.ОбновитьИсторию()» имеет смысл только выборочно — при массовых изменениях данных в обработках.

    А вот, про перенос истории мне вообще не ясно — ЗАЧЕМ?

    Как и про чистку таблицы «dbo._DataHistoryQueue0», она что — сама при вызове указанных выше функций записи истории не очищается?

    Reply
  2. 123456qwerty

    (1)

    Это все написано для тех кто использует стандартные механизмы платформы и не изобретает велосипед с регистрами.

    Проблема которая здесь описана, связана с необходимостью вести историю данных исключая хранения ненужных версий данных, и необходимостью переноса истории данных, если объект был перемещен в другой справочник, или документ. При такой реализации использовать ИсторияДанных.ОбновитьИсторию() не получится, и очередь версий данных сама не почистится, а накопление очереди приведет к необоснованному росту БД со всеми вытекающими проблемами.

    Вероятнее всего Вам не понятна статья, потому что вы с такими проблемами в своей работе не встречались.

    Reply
  3. Darklight

    (2)да, вероятно не встречался — ибо эта встроенная история бред бредом! Но статья у Вас всё равно сумбурно написана!

    Reply
  4. Glebis
    объект пересоздается в другом месте БД, необходимо перенести и его историю

    . Несколько раз перечитал, но задачу «Б» так и не понял: что значит пересоздается, GUID остается?. Что за «другое место БД»?. Объекты одного типа или разных? У этих объектов часть реквизитов совпадает?

    Reply
  5. 123456qwerty

    (4)

    Есть два документа1) Оборудование подразделения и 2) Демонтированное оборудование подразделения

    В первом хранится оборудование, эксплуатируемое подразделением. В первом учитываются ремонты, простои, плановые остановки

    Второй документ хранит оборудование, выведенное из эксплуатации и затраты по этому оборудованию за весь цикл его жизни.

    Два документа имеют часть реквизитов одинаковых, а часть разных. Во-втором документе ТЧ аналогичная первому документу скрыта на форме и при переносе документа не заполняется. Пустая. А ТЧ затрат заполнена.

    При выводе из эксплуатации оборудование из первого документа удаляется, а во-втором создается путем копирования одинаковых реквизитов(кроме ТЧ) и выборками из регистров затрат.

    Время от времени специалистам необходимо видеть, какие работы проводились с оборудованием и кто вносил изменения в документ.

    Так же демонтированное оборудование иногда возвращают в работу.

    В связи с этим есть необходимость видеть всю историю по данному оборудованию.

    GUID у объектов разные

    Объекты одно типа

    У этих объектов совпадают реквизиты только те, по которым необходимо видеть историю.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *