Каталог решений - Триггер для сохранения таблицы Config перед динамическим обновлением.

Триггер для сохранения таблицы Config перед динамическим обновлением.

Триггер для сохранения таблицы Config перед динамическим обновлением.

В наличии

Вначале хотел выложить триггер в своей предыдущей статье http://infostart.ru/public/324751/, но потом понял, что просто выложить не получится, необходимо еще и объяснить, а поскольку материала оказалось слишком много, то пришлось выносить в отдельную статью, и так максимально обрезанную, оставил только самое необходимое.

Категория:

Описание

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

Нет ничего сложного в том, чтобы поместить на табличку триггер, который будет сохранять ее перед тем, как в нее будут внесены изменения, но проблема в том, что при внесении изменений в конфигурацию базы данных происходит множество действий: добавление, удаление и изменение уже существующих записей в таблице. А поскольку процесс длительный и непрерывный, то сложность заключается в том, чтобы определить, в какой момент времени необходимо сохранять данные, а в какой ничего не делать. Таким образом, задача сводится к тому, что нужно определить, когда обновление началось и когда оно закончилось.

Так как у меня MS SQL, то логика была такая:

1)      Создаем табличку [master].[dbo].[sp_backup_config_tables] полную копию Config.

2)      Ставим триггер на таблицу [Config] который при первом выполнении сохраняет таблицу в [sp_backup_config_tables] , а также добавляет еще одну запись в нее (признак того, что идет обновление) , а все последующие транзакции к таблице проверяются на этот признак и, если он есть, то ничего не делают, и на последней транзакции мы удаляем этот признак из таблицы.

Довольно простой алгоритм. Единственная сложность в котором была, это определить окончание обновления, то есть момент, когда этот признак удалить из таблички. Для этого пришлось понять, что происходит в момент обновления конфигурации. Написал три простеньких триггера, соответственно на: добавление, удаление и обновление. Которые писали бы информацию о своем действии в таблицу [Master].[dbo].[Config_Insert] :

 

Создание таблицы:


SET ANSI_NULLS ON
GO
 
SET QUOTED_IDENTIFIER ON
GO
 
CREATE TABLE [dbo].[Config_Insert](
      [FileName] [nvarchar](128) NULL,
      [NomerZapis] [int] NULL,
      [TipOper] [nvarchar](128) NULL
) ON [PRIMARY]


Триггеры: 


GO
USE [ТестоваяБаза]
IF OBJECT_ID ('TEST_Trigger_1C_Config_insert', 'TR') IS NOT NULL
   DROP TRIGGER TEST_Trigger_1C_Config_insert;
GO
CREATE TRIGGER TEST_Trigger_1C_Config_insert
ON [ТестоваяБаза].[dbo].[Config]
for insert
AS
SET NOCOUNT on
insert into [Master].[dbo].[Config_Insert] (FileName,NomerZapis,TipOper) values ((select filename from inserted),(select  COUNT (*) from [Master].[dbo].[Config_Insert])+1,'insert');
 
-----------------------------------------------------------------------------------
GO
USE [ТестоваяБаза]
IF OBJECT_ID ('TEST_Trigger_1C_Config_delete', 'TR') IS NOT NULL
   DROP TRIGGER TEST_Trigger_1C_Config_delete;
GO
CREATE TRIGGER TEST_Trigger_1C_Config_delete
ON [[ТестоваяБаза].[dbo].[Config]
for delete
AS
SET NOCOUNT on
insert into [Master].[dbo].[Config_Insert] (FileName,NomerZapis,TipOper) values ((select filename from deleted),(select  COUNT (*) from [Master].[dbo].[Config_Insert])+1,'delete');
-----------------------------------------------------------------------------------
GO
USE [ТестоваяБаза]
IF OBJECT_ID ('TEST_Trigger_1C_Config_update', 'TR') IS NOT NULL
   DROP TRIGGER TEST_Trigger_1C_Config_update;
GO
CREATE TRIGGER TEST_Trigger_1C_Config_update
ON [ТестоваяБаза].[dbo].[Config]
for update
AS
SET NOCOUNT on 
insert into [Master].[dbo].[Config_Insert] (FileName,NomerZapis,TipOper) values ((select filename from inserted),(select  COUNT (*) from [Master].[dbo].[Config_Insert])+1,'update');

Получилось много статистической информации, к примеру: 

 

 

После анализа нескольких таких таблиц выяснил, что достаточно одного триггера на добавление.

 


GO
USE [ТестоваяБаза]
IF OBJECT_ID ('TEST_Trigger_1C_Config_insert', 'TR') IS NOT NULL
   DROP TRIGGER TEST_Trigger_1C_Config_insert;
GO
CREATE TRIGGER TEST_Trigger_1C_Config_insert
ON [ТестоваяБаза].[dbo].[Config]
for insert
AS
SET NOCOUNT on
 
insert into [Master].[dbo].[Config_Insert] (FileName,NomerZapis,TipOper) values ((select filename from inserted),(select  COUNT (*) from [Master].[dbo].[Config_Insert])+1,'insert');

Так как даже на данном примере видно, что 1С сперва производит добавление новых записей в таблицу, а уже потом их обрабатывает. 

 

В ходе анализа статистической информации из таблицы [Master].[dbo].[Config_Insert] выяснилось, что само обновление представляет из себя просто последовательную запись в таблицу строк содержащих файлы метаданных и файлы описаний конфигурации (обезательные “root”,”version”,”versions”) , а также, что существует несколько вариантов обновления (если судить по изменениям в таблице Config) :

1)      Обычное штатное обновление

2)      «Первое» динамическое обновление (всякое динамическое обновление считается «первым» если оно выполнено первый раз после шатного, так как в таблице Config появляется файл “DynamicallyUpdated” – в котором хранится идентификатор динамического обновления и добавляются файлы измененных метаданных в результате этого динамического обновления в виде “c03b12af-b17c-4e98-b06f-c09e5b8f9fa1_dynupdate_d94ab89a-8c1f-4c62-bec3-c8af26df7864” , где слева от “_dynupdate_” идентификаторо метаданного, а справа идентификатор динамического обновления).

3)      Последующие динамические обновления (все динамические обновления после «первого»)

4)      Штатное обновление после динамического (при шатном обновлении происходит реструктуризация таблицы Config и стирается вся информация о динамическом обновлении, удаляются все записи с “_dynupdate_” и запись “DynamicallyUpdated”)

5)      Отказ от внесения изменений (при любом из вышеперечисленных изменений, конфигуратор попросит вас подтвердить внесения изменения, но при этом в таблицу Config уже будут добавлены записи об изменениях с дополнением “_new” к имени файла и, если вы откажитесь, то они просто будут удалены.)

 

Я смог выделить только эти варианты (возможно есть еще что-то, что я не увидел).

Именно 5-ый пункт стал решающим при выборе типа триггера, так как мы должны закончить все наши «манипуляции» до момента «принятия решения» (подтверждения обновления в конфигураторе). По этому тип триггера “insert” .

Далее оставалось только учесть в алгоритме триггера все 4 варианта обновления.

Во всех 4 случаях будет запись с именем “ root” – так что её можно использовать как признак окончания операции. И, исключив из проверки частные случаи первых 4-х пунктов в виде записей ‘dbStruFinal’, ‘dynamicCommit’, ‘DynamicallyUpdated’, ‘root’, ‘version’, ‘version’, получаем рабочий триггер:

Сам триггер:

 

GO
USE [РабочаяБаза]
--Проверяем есть ли тригер,если есть, то удаляем
IF OBJECT_ID ('KAS_TEST_Trigger_1C_Config', 'TR') IS NOT NULL
   DROP TRIGGER KAS_TEST_Trigger_1C_Config;
GO
--Добавляем триггер
CREATE TRIGGER KAS_TEST_Trigger_1C_Config
ON [РабочаяБаза].[dbo].[Config]
for insert
AS
SET NOCOUNT on
--Объявляем переменные
declare @Int_1C_Vrem_Priznak int
declare @Int_1C_commit int
 
--Необходимо определить первую и последнюю транзакцию.
--Если в таблици нет записи FileName like '%Vrem_Priznak%'
--Значит транзакция первая.
--Так как существеует два типа обновления Обычное и Динамическое то и их признаки разные,
--но все записи идут после 'commit',так что его можно использовать как признак последней итерации
--исключив дальнейшие варианты с 'dbStruFinal','dynamicCommit','DynamicallyUpdated','root','version','versions'
 
select @Int_1C_Vrem_Priznak = COUNT (*) from [master].[dbo].[sp_backup_config_tables] where FileName like '%Vrem_Priznak%'
select @Int_1C_commit= COUNT (*)from inserted where filename like 'dbStruFinal' or filename like 'dynamicCommit' or filename like 'DynamicallyUpdated' or filename like 'root' or filename like 'version' or filename like 'versions' 

if @Int_1C_Vrem_Priznak = 0 and @Int_1C_commit=0
BEGIN
truncate table [master].[dbo].[sp_backup_config_tables];
insert into [master].[dbo].[sp_backup_config_tables] select * from [РабочаяБаза].[dbo].[Config];
delete  [master].[dbo].[sp_backup_config_tables] where FileName in (select filename from inserted)
insert into [master].[dbo].[sp_backup_config_tables] (filename,Creation,Modified,Attributes,DataSize,BinaryData) values ('Vrem_Priznak','01.01.01','01.01.01',0,1,'0x01')
-- для 8.3
--insert into [master].[dbo].[sp_backup_config_tables] (filename,Creation,Modified,Attributes,DataSize,BinaryData,PartNo) values ('Vrem_Priznak','01.01.01','01.01.01',0,1,'0x01',0)
end
 
select @Int_1C_commit= COUNT (*) from inserted where filename like 'commit'

if @Int_1C_commit<> 0 
BEGIN
delete [master].[dbo].[sp_backup_config_tables] where filename like '%Vrem_Priznak%' 
end
 

Данный триггер проверен на платформах 8.2.19.83 и 8.3.5.1231 . И исправно служит для конфигруций «Комплексная автоматизация», «Зарплата и Управление персоналом», «Бухгалтерияя предприятия 2.0» и «Бухгалтерия предприятия 3.0». 

Если надумаете использовать его, то перед выполнением  

insert into [РабочаяБаза].[dbo].[Config] select * from [master].[dbo].[sp_backup_config_tables]

Отключите триггер.

P.s. Если у кого-то возникнут ошибки при работе триггера или какие-либо дополнения, то не стесняемся, оставляем комменты или, если стесняемся, пишем в личку:).

has been added to your cart:
Оформление заказа