Аудит змін даних

Введення аудиту підвищує рівень цілісності даних в базі Повноцінний журнал аудиту може містити масу інформації, в тому числі наведену нижче

■ Всі зміни даних у рядках з моменту їх початкової вставки

■ Всі зміни даних, внесені конкретним користувачем за останній тиждень

■ Всі зміни даних, внесені на конкретній робочій станції під час обідньої перерви

■ Всі зміни даних, внесені клієнтськими додатками, що відрізняються від стандартного

Ведення журналу аудиту даних вирішує безліч істотних проблем як для адміністраторів баз даних, так і для кінцевих користувачів Одного разу моя консультаційна компанія розробила систему управління юридичними документами для компанії зі списку Fortune 100 Цю базу даних постійно повинна була поповнювати регуляторними законами юридична фірма Одного разу ця фірма вибилася з графіка і пояснила це тим, що не могла протягом двох тижнів вводити дані через проблеми з програмним забезпеченням Коли ми вивели список з більш 70000 змін даних, зафіксованих за ці два тижні в журналі аудиту даних, питання пошуку винного розвязався сам собою

Мені доводилося зустрічати в публікаціях методи аудиту даних, що додають в таблицю декілька стовпців або дублюючі таблиці для збереження останніх змін Жоден з цих методів не можна назвати прийнятним Частковий аудит і аудит останніх змін не мають практично ніякого сенсу Журнал аудиту повинен постійно зберігати всі зміни даних, інакше будь, хто зрозуміє його систему дії, зможе внести ще одну зміну і тим самим назавжди стерти початкове значення

Журнал аудиту

Завданням таблиці (або журналу) аудиту (Audit) є організація єдиного місця зберігання всіх змін в базі даних Пропонована нижче таблиця журналу аудиту здатна зберігати всі невеликі зміни (НЕ BLOB), що вносяться в усі таблиці Стовпець Operation зберігає значення, що визначають характер інструкції DML, відповідальної за зміни (I, і чи D):

CREATE TABLE dboAudit (

AuditID UNIQUEIDENTIFIER RowGUIDCol NOT NULL CONSTRAINT DF_Audit_Audi11D DEFAULT (NEWID() )

CONSTRAINT PK_Audit PRIMARY KEY NONCLUSTERED (AuditID),

AuditDate DATETIME NOT NULL,

SysUser VARCHAR(50) NOT NULL,

Application VARCHAR(50) NOT NULL,

TableName VARCHAR(50)NOT NULL,

Operation CHAR(l) NOT NULL,

PrimaryKey VARCHAR (50) NOT NULL,

RowDescription VARCHAR(50) NULL,

SecondaryRow VARCHAR (50) NULL,

[Column] VARCHAR(50) NOT NULL,

OldValue VARCHAR(50) NULL,

NewValue VARCHAR(50) NULL )

У стовпці PrimaryKey зберігається покажчик на змінену рядок, а в стовпці RowDescription-осмислене опис цього рядка Ці два стовпці дозволяють створити обєднання з вихідною таблицею і отримати безпосереднє опис операції Значення стовпця PrimaryKey має першорядну важливість, оскільки дозволяє швидко відшукати всі зміни в деякій рядку, незалежно від того, як опис зміниться з часом

Фіксований тригер журналу аудиту

Найбільш простий метод аудиту даних використовує тригери для всіх таблиць, перевіряючі всі стовпці за допомогою функції updated () і записуючі знайдені зміни в таблицю аудиту

Інструкція INSERT обєднує таблиці Inserted і Deleted, щоб коректно обробити всі багаторядкові вставки і оновлення Це обєднання є лівим зовнішнім, так що операції вставки, що містять рядки тільки в таблиці Inserted, будуть все одно записані в журнал аудиту Дане обєднання також обмежено умовою, що гарантує, що якщо багаторядкове оновлення зачіпає стовпці тільки декількох рядків з цієї множини, то тільки ці зміни будуть записані в таблицю аудиту

У наведеному нижче прикладі тригера журналу аудиту відслідковуються зміни в таблиці Product бази даних OBXKites

CREATE TRIGGER Product_Audit ON dboProduct AFTER Insert, Update NOT FOR REPLICATION AS

DECLARE (©Operation CHAR(l)

IF EXISTS(SELECT * FROM Deleted)

SET (©Operation = U

ELSE

SET (©Operation = 11

IF UPDATE(ProductCategorylD)

INSERT dboAudit

(AuditDate, SysUser, Application, TableName, Operation,

PrimaryKey, RowDescription, SecondaryRow, [Column],

OldValue, NewValue)

SELECT GetDate(), suser_sname(), APP_NAME(), Product, (©Operation, InsertedProductID, InsertedCode,

NULL, 1ProductCategorylD1,

ОРСProductcategoryName, NPCProductcategoryName FROM Inserted

LEFT OUTER JOIN Deleted

ON InsertedProductID = DeletedProductID AND InsertedProductCategorylD

&lt&gt DeletedProductCategorylD LEFT OUTER JOIN dboProductCategory OPC ON DeletedProductCategorylD = OPCProductCategorylD JOIN dboProductCategory NPC ON InsertedProductCategorylD = NPCProductCategorylD

IF UPDATE(Code)

INSERT dboAudit

(AuditDate, SysUser, Application, TableName, Operation, PrimaryKey, RowDescription, SecondaryRow, [Column], OldValue, NewValue)

SELECT GetDate(), suser_sname(), APP_NAME(),

‘Product, ©Operation, InsertedProductID,

InsertedCode, NULL, Code,

DeletedCode, InsertedCode FROM Inserted

LEFT OUTER JOIN Deleted

ON InsertedProductID = DeletedProductID AND InsertedCode &lt&gt DeletedCode

IF UPDATE(ProductName)

INSERT dboAudit

(AuditDate, SysUser, Application, TableName, Operation, PrimaryKey, RowDescription, SecondaryRow, [Column], OldValue, NewValue)

SELECT GetDate(), suser_sname(), APP_NAME(),

‘Product, ©Operation,

InsertedProductID, InsertedCode, NULL, Name, DeletedProductName, InsertedProductName FROM Inserted

LEFT OUTER JOIN Deleted

ON InsertedProductID = DeletedProductID

AND InsertedProductName &lt&gt DeletedProductName

IF UPDATE(ProductDescription)

INSERT dboAudit

(AuditDate, SysUser, Application, TableName, Operation, PrimaryKey, RowDescription, SecondaryRow, [Column], OldValue, NewValue)

SELECT GetDateO , suser_sname () , APP_NAME“&lt) / Product , ©Operation, InsertedProductID, InsertedCode,

NULL, ProductDescription,

DeletedProductDescription,

InsertedProductDescription FROM Inserted

LEFT OUTER JOIN Deleted

ON InsertedProductID = DeletedProductID AND InsertedProductDescription &lt&gt DeletedProductDescription

IF UPDATE(ActiveDate)

INSERT dboAudit

(AuditDate, SysUser, Application, TableName, Operation,

PrimaryKey, RowDescription, SecondaryRow, [Column],

OldValue, NewValue)

SELECT GetDate() , suser_sname() , APP_NAME() , 1 Product1, @Operation, InsertedProductID, InsertedCode,

NULL, ActiveDate,

DeletedActiveDate, InsertedActiveDate FROM Inserted

LEFT OUTER JOIN Deleted

ON InsertedProductID = DeletedProductID

AND InsertedActiveDate = DeletedActiveDate

IF UPDATE(DiscontinueDate)

INSERT dboAudit

(AuditDate, SysUser, Application, TableName, Operation,

PrimaryKey, RowDescription, SecondaryRow, [Column],

OldValue, NewValue)

SELECT GetDate(), suser_sname(), APP_NAME() , Product1, ©Operation, InsertedProductID, InsertedCode,

NULL, DiscontinueDate1,

DeletedDiscontinueDate, InsertedDiscontinueDate FROM Inserted

LEFT OUTER JOIN Deleted

ON InsertedProductID = DeletedProductID AND InsertedDiscontinueDate

!= DeletedDiscontinueDate

Якщо встановити цей тригер аудиту, то наступний пакет вставки і оновлення даних про товари, що використовує дві інструкції DML, а також створені раніше збережені процедури, дозволить його перевірити Перший тест використовує збережену процедуру pProduct_AddNew:

Ехес pProduct_AddNew Kite, 200, The MonstaKite,

‘Man what a big Kite’

SELECT TableName, RowDescription, [Column], NewValue FROM dboAudit

Буде отримано наступний результат:

TableName RowDescription

Column                                            NewValue

Product    2 00                                    ProductCategorylD        Kite

Product    200                                     Code    200

Product    2 00                                    Name   The MonstaKite

Product    200                                     ProductDescription       Man what a big Kite

Product    200                                     ActiveDate       Mar 1 2 0 02 1:35PM

Product    2 00                                    DiscontinueDate           NULL

Тригери є відмінним засобом ведення журналу аудиту, так як реєструють всі зміни, навіть виконувані прямим введенням інструкцій DML У наступному прикладі ми не будемо використовувати збережені процедури і виконаємо звичайну інструкцію UPDATE Після цього в журналі аудиту ми побачимо як старе, так і нове значення:

UPDATE dboProduct

SET ProductDescription = Biggie Sized

WHERE Code = 2 00

Наступний запит витягне для нас історію стовпця ProductDescription для товару з кодом 200:

SELECT AuditDate, OldValue, NewValue FROM dboAudit WHERE TableName = 1 Product1 AND RowDescription = 200

AND [Column] = ProductDescription

Отримаємо наступний результат:

AuditDate                                OldValue                          NewValue

2002-03-01 13:35:17093 NULL                                     Man what a

big Kite

2002-03-01 15:10:49257 Man what a                             Biggie Sized

big Kite

Виконання відкату операцій за допомогою журналу аудиту

У повноцінній системі аудиту користувач може побачити всі зміни конкретної рядки, внесені з моменту її створення У цьому списку користувач може вибрати будь-яку операцію і відкотити її назад Як тільки обрана деяка рядок журналу аудиту, відкат операції зводиться до звичайного виконання інструкції UPDATE, що використовує дані журналу аудиту

У наступному прикладі продемонстровано відкат зміни, зареєстрованого в журналі аудиту Збережена процедура pAudit_RollBack приймає як параметр первинний ключ таблиці Audit, після чого створює динамічну інструкцію UPDATE, що містить коректні таблицю, рядок, стовпець і відновлюване значення:

CREATE PROCEDURE pAudit_RollBack (

@AuditID UNIQUEIDENTIFIER)

AS

SET NoCount ON

DECLARE

@SQLString NVARCHAR(4000),

@TableName NVARCHAR(50),

@PrimaryKey NVARCHAR(50),

@Column NVARCHAR(50),

@NewValue NVARCHAR(50)

SELECT

@TableName = TableName,

@PrimaryKey = PrimaryKey,

@Column = [Column],

@NewValue = OldValue

FROM dboAudit

WHERE AuditID = @AuditID

SET @SQLString =

‘UPDATE + @TableName

+ SET + @Column +=”+ @NewValue +”’

+ WHERE + ©TableName + ID = ” + ©PrimaryKey + 11,1 EXEC sp_executeSQL @SQLString Return

Маючи у своєму розпорядженні дану збережену процедуру, наступний сценарій імітує логіку, необхідну для відкату операції оновлення Як ви памятаєте, вихідним описом

товару з кодом 200 було Man what a big Kite; під час тестування тригера журналу аудиту воно було змінено на Biggie Sized. Наступний сценарій знаходить рядок, відповідну цій зміні, і передає ідентифікатор GUTO процедурі pAudit_RollBack, яка, в свою чергу, відкатує дане оновлення:

DECLARE @AuditRollBack UNIQUEIDENTIFIER

SELECT @AuditRollBack = AuditID FROM dboAudit WHERE TableName = Productr AND RowDescription = 200

AND OldValue = ’Man what a big Kite I

EXEC pAudit_RollBack @AuditRollBack

SELECT ProductDescription FROM dboProduct WHERE Code = 2 00

Результат виконання пакету:

ProductDescription

Man what a big Kite

Ця процедура відкотила тільки одне конкретне зміна, однак її можна модифікувати так, щоб можна було зробити відкат до заданої точки в часі Для цього з журналу аудиту слід витягти історію рядка і в циклі виконати відкати всіх операцій в зворотному порядку

Складнощі аудиту

Крім додаткового часу, необхідного на програмування, ведення аудиту тягне за собою й інші складності

Спроектуйте всю базу даних і перевірте коректність її схеми ще до реалізації системи аудиту Внести зміни в схему буде набагато складніше, коли тригери журналу аудиту вже будуть в наявності

Аудит повязаних даних

Найбільшу складність представляє аудит повязаних даних, таких як вторинні рядка Наприклад, внесення змін до рядок замовлення фактично змінює все замовлення Коли користувач захоче переглянути всю історію замовлення, він повинен побачити всі зміни, повязані з ним Таким чином, внесення зміни в таблицю рядків замовлення має бути записано і як зміна таблиці замовлень, при цьому номер зміненої рядка замовлення повинен бути відображений в стовпці Secondary Row

Запис змін, що вносяться в зовнішні ключі, являє собою ще один складний аспект повноцінного аудиту Користувачеві навряд чи захочеться бачити новий ідентифікатор GUID або значення ідентичності Якщо, наприклад, спосіб доставки товару змінився з Slow Boat на Speedy Express, то тригер журналу аудиту повинен буде перейти по зовнішньому ключу і записати в журнал зрозуміле користувачу значення У наведеному раніше прикладі фіксованого тригера Product_Audit зміни, що вносяться до стовпець ідентифікатора категорії товару, призводять до запису в журнал аудиту назви цієї категорії

Створюються і змінюються дати

При використанні повноцінної системи аудиту з журналу можна витягти дати створення і останньої зміни будь-якого рядка На практиці ж в багатокористувацької середовищі з великим потоком даних я настійно рекомендую виконати денормализация і винести ці дати безпосередньо в таблицю аудиту

Додаткова У главі 23 був описаний тригер, відновлювальний створені та оновлені стовп-чфорМсщіг, ци і одночасно запобігає проблеми, повязані з рекурсією

Аудит інструкцій SELECT

Тригери аудиту даних обмежені інструкціями INSERT, UPDATE і DELETE Для реалізації аудиту читання даних використовуйте збережену процедуру fetch Використовуйте засоби захисту інформації SQL Server для обмеження доступу до таблиць при цьому створюйте систему таким чином, щоб всі операції читання виконувалися за допомогою збереженої процедури або функції

Аудит даних і питання безпеки

Ще одним питанням, повязаним з веденням журналу аудиту, є забезпечення його захисту Кожен, хто має право читання цього журналу, одночасно отримує неявний доступ до інформації всіх обслуговуваних ним таблиць Якщо користувачі повинні мати можливість переглядати історію конкретних рядків, використовуйте для доступу до даних аудиту збережену процедуру

Аудит даних і продуктивність

Повноцінний журнал аудиту даних додає на систему певне навантаження Будь вставка одного рядка в таблицю з двадцятьма стовпцями спричинить за собою виконання двадцяти інструкцій вставки в таблицю аудиту Щоб трохи знизити додаткове навантаження, прийміть наступні заходи

І Обмежте число індексів у таблиці аудиту

І Розміщуйте таблицю аудиту в окремій файлової групі в окремій дискової підсистемі Наявність окремої файлової групи також спростить та виконання резервного копіювання

■ Використовуючи фіксовані тригери аудиту, обмежтеся тільки тими стовпцями, які вимагають високого рівня цілісності даних

Динамічні тригери і процедури журналу аудиту

Провівши кілька місяців за написанням фіксованих тригерів аудиту для проекту з сотнями таблиць, я однозначно прийшов до рішення використовувати динамічну систему аудиту

Динамічний журнал аудиту вимагає невеликих тригерів в кожній з таблиць Ці тригери всього лише копіюють таблиці Inserted і Deleted в тимчасові, після чого передають кілька змінних збереженій процедурі, на яку покладена основна робота Та перевіряє двійкове значення Columns_Updated, після чого визначає правильне значення, яке використовується замість порожнього, а потім генерує динамічну інструкцію SQL, що виконує запис в журнал аудиту

Ось у чому полягає ключовий момент Для виконання динамічної інструкції SQL потрібно виклик системної збереженої процедури sp_execSQL, яка функціонує у вкладеному пакеті Т-SQL Область визначення таблиць Inserted і Deleted обмежена тригером – ці таблиці недоступні викликає процедурою або динамічної інструкції SQL Саме тому і створюються тимчасові таблиці, які вже й передають зміни в збережену процедуру і згодом в динамічний запит SQL Область визначення тимчасової таблиці включає в себе викликає збережену процедуру і виконувані модулі, що і робить можливим створення динамічного тригера аудиту

Природно, будь-який метод має свої переваги і недоліки З позитивного боку метод тригер / збережена процедура / динамічний SQL надзвичайно легко реалізувати Він здатний працювати практично з будь-якими таблицями У той же час існують і деякі обмеження цієї версії динамічного тригера аудиту

■ Для передачі значень таблиць Inserted і Deleted в збережену процедуру він використовує тимчасові таблиці, тому його важко назвати найшвидшим методом До того ж тимчасові таблиці створюються за допомогою синтаксису SELECT INTO, що повязано з додатковими питаннями продуктивності та блокування

■ Поточний динамічний журнал аудиту не відстежує автоматично повязані дані і вторинні таблиці Він також не здатний вести аудит таблиць з складовими первинними ключами

■ Він не веде аудит таблиць із стовпцями BLOB (типів image, t axt і ntext), оскільки їх не можна витягти з таблиць Inserted і Deleted

Виходячи з цих обмежень, рекомендується використовувати метод динамічного аудиту тільки для таблиць, що не оновлюються часто, а також для баз даних, які при використанні цього методу мають нормальні показники продуктивності Для забезпечення високої продуктивності краще використовувати метод фіксованих тригерів аудиту, в яких прошитий склад стовпців в той же час цей метод передбачає набагато більше програмування і складності в супроводі

Зважаючи на вищесказане, наведемо приклад програмного коду ведення динамічного журналу аудиту:

/*

Створення динамічних журналу і тригера аудиту

Пол Нільсен

У цьому прикладі до таблиць Customers і Products бази даних Northwind додається динамічний тригер аудиту

Версія 11-6 серпня 2001 * /

USE Northwind

– Створення таблиці для зберігання журналу аудиту IF Exists (SELECT * FROM sysobjects WHERE NAME = Audit) DROP TABLE Audit

Go

CREATE TABLE dboAudit (

AuditID UNIQUEIDENTIFIER ROWGUIDCOL NOT NULL CONSTRAINT DF_Audit_AuditID DEFAULT (NEWIDO)

CONSTRAINT PK_Audit PRIMARY KEY NONCLUSTERED (AuditID), AuditDate DATETIME NOT NULL,

SysUser VARCHAR(50) NOT NULL,

Application VARCHAR(50) NOT NULL,

TableName VARCHAR(50)NOT NULL,

Operation CHAR(l) NOT NULL,

PrimaryKey VARCHAR(50) NOT NULL,

— RowDescription VARCHAR(50) NULL,

SecondaryRow VARCHAR(50) NULL,

[Column] VARCHAR(50) NOT NULL,

OldValue VARCHAR(50) NULL,

NewValue VARCHAR(50) NULL )

GO

– Створення функції для імітації значення Columns_Updated () IF EXISTS (SELECT *

FROM sysobjects

WHERE NAME = GenColUpdated)

DROP FUNCTION GenColUpdated

Go

CREATE FUNCTION dboGenColUpdated (OCol INT, @ColTotal INT)

RETURNS INT AS

BEGIN

– Ця функція імітує Columns_Updated ()

DECLARE

@ColByte INT,

@ColTotalByte INT,

@ ColBit INT – Обчислення позицій байтів

SET @ColTotalByte = 1 + ((@ColTotal-l) /8)

SET @ColByte = 1 + ((@Col-1)/8)

SET @ColBit = @col – ((@colByte-l) * 8)

– Генерація значення Columns_Updated () для даного стовпця RETURN

POWER(2, @colbit + ((@ColTotalByte-@ColByte) * 8)-l)

END

go

– Створення збереженої процедури динамічного аудиту IF EXISTS (SELECT * FROM SysObjects WHERE NAME = pAudit) DROP PROC pAudit

Go

CREATE PROCEDURE pAudit (

@Col_Updated VARBINARY(1028),

@TableName VARCHAR(100),

@PrimaryKey SYSNAME)

AS

SET NoCount ON DECLARE

@ColTotal INT,

@ColCounter INT,

@ColUpdatedTemp INT,

@ColName SYSNAME,

@BlankString CHAR(l),

@SQLStr NVARCHAR(1000),

@ColNull NVARCHAR(50),

@SysUser NVARCHAR(100),

@ColumnDataType INT,

@IsUpdate BIT,

@tempError INT SET @SysUser = suser_sname()

SET @BlankString = 1

– Ініціалізація змінних стовпців SELECT @ ColCounter = 0 SELECT @ ColTotal = Count (*)

FROM SysColumns JOIN SysObjects

ON SysColumnsid = SysObjectsid WHERE SysObjectsname = @ TableName – Установка прапора IsUpdated IF EXISTS (SELECT * FROM # tempDel)

SELECT @IsUpdate = 1 ELSE

SELECT @ IsUpdate = 0 – Оновлення стовпців

WHILE ((SELECT @ColCounter) 1= @ColTotal)

BEGIN

SELECT @ColCounter = @ColCounter + 1 SET @ColUpdatedTemp

= dboGenColUpdated(@ColCounter,@ColTotal)

– Бітовий AND між оновлюваними битами – і бітом вибраного стовпця

IF (@Col_Updated &amp @ColUpdatedTemp) = @ColUpdatedTemp BEGIN

SET @ColNull = null SELECT

@ColName = SysColumns[name],

– Отримання імені стовпця і типу даних

@ColumnDataType = SysColumnsxtype FROM SysColumns JOIN SysObjects

ON SysColumnsid = SysObjectsid WHERE SysObjects[NAME] = @TableName

and SysColumnsColID = @ColCounter IF @ColName NOT IN (Created1, Modified)

BEGIN – текстові стовпці

IF @ColumnDataType IN

( 175, 239, 99, 231, 35, 231, 98, 167 )

SET @ColNull =

– Числові і бітові стовпці

ELSE IF @ColumnDataType IN

( 106, 62, 56, 60, 108, 59, 52, 122, 104 SET @ColNull = O

– Стовпці дат

ELSE IF @ColumnDataType IN ( 61, 58 )

SET @ColNull = 1/1/1980

– Стовпці унікальних ідентифікаторів

ELSE IF @ColumnDataType IN ( 36 ) SET @ColNull = ■■■■■

IF @ColNull IS NOT NULL BEGIN

IF @ IsUpdate = 1 – потрібно скорегувати відступи SET @ SQLStr =

‘ Insert Audit(TableName, PrimaryKey, SysUser, [Column], + AuditDate, Application, OldValue, NewValue,Operation) + Select + ©TableName + 1 1 ,

#templn[+ ©PrimaryKey + ],

111 + ©SysUser + 11, +

”’1 + ©ColName + 1‘, GetDate(), App_Name(),1 +

‘ IsNul1(convert(nvarchar(100) ,

#tempDel[1 + ©ColName + ]),’&ltnull&gt11), +

1 IsNull(convert(nvarchar(100) ,

#templn[ + @ColName + ]),’&ltnull&gt”),’U” +

1 From #templn +

1 Join #tempDel1 +

‘ On #templn[’ + @PrimaryKey + ]

= #tempDel [1 + ©PrimaryKey + ]1 +

‘ AND isnull(#templn1 + @ColName + , + @ColNull + )

!= isnull(#tempDel1 + ©ColName + , + @ColNull + )

+ ’ Where Not (#templn[ + @ColName + ] Is Null and #tempDel[ + @ColName + ] Is Null)1 ELSE – Вставка SET @ SQLStr =

‘ Insert Audit(TableName, PrimaryKey, SysUser, [Column], + AuditDate, Application, OldValue, NewValue,Operation) + Select ”+ @TableName + 111,#templn[+ @PrimaryKey + ] , + @SysUser + , +

‘ 4- ©ColName + , GetDateO , App_Name() , 1 +

1&nbsp&nbsp&nbsp Null, +

‘ IsNull(convert(nvarchar(100),

#templn[ + ©ColName +]),’&ltnull&gt”),’I” +

‘ From #templn +

‘ Where Not (#templn[ + @ColName + ] Is Null)

EXEC sp_execuuesql ©SQLStr SET ©TempError = ©@Error IF ©TempError &lt&gt 0 BEGIN

– Включайте відкат, тільки якщо хочете,

– Щоб помилка при запису в журнал аудиту – скасовувала операцію модифікації даних – Відкат

RAISERROR (Помилка запису в журнал аудиту, 15, 1)

END

END END END END RETURN Go

– Приклади табличних тригерів

– Вони повинні бути додані до всіх таблиць – з настройками імені таблиці і первинного ключа – Тригер для таблиці Products IF EXISTS (SELECT *

FROM sysobjects

WHERE NAME = Products_Audit)

DROP TRIGGER Products_Audit

Go

CREATE TRIGGER Products_Audit ON dboProducts AFTER Insert, Update NOT FOR REPLICATION

AS

DECLARE

@Col_Updated VARBINARY(1028),

@TableName VARCHAR (10 0) ,

@PrimaryKey SYSNAME SET NoCount ON

– Налаштування даних для аудиту – Установка імені таблиці SET @ TableName = Products

– Налаштування стовпця, що ідентифікує рядок SET @ PrimaryKey = ProductID

SET @Col_Updated = Columns_Updated()

SELECT * INTO #TempIn FROM Inserted

SELECT * INTO #TempDel FROM Deleted

– Виклик процедури, що зберігається аудиту

Ехес pAudit @ Col_Updated, @ TableName, @ PrimaryKey

Go

– Тригер для таблиці Customers IF EXISTS (SELECT *

FROM SysObjects

WHERE [NAME] = ’Customers_Audit’)

DROP TRIGGER Customers_Audit

Go

CREATE TRIGGER Customers_Audit ON dboCustomers AFTER Insert, Update NOT FOR REPLICATION

AS

DECLARE

@Col_Updated VARBINARY(1028),

@Tab1eName VARCHAR(10 0),

@PrimaryKey SYSNAME SET NoCount ON SET @TableName = ’Customers’

SET @PrimaryKey = CustomerlD

SET @Col_Updated = Columns_Updated()

SELECT * INTO #TempIn FROM Inserted SELECT * INTO #TempDel FROM Deleted EXEC pAudit @Col_Updated, @TableName, @PrimaryKey

У сценарії DynamicAudit sql, який можна завантажити з сайту книги (www SQLServerBiblecom), міститься кілька прикладів вставки і видалення, а також запросив вилучення даних з таблиці аудиту, при обєднанні її з таблицею товарів

Джерело: Нільсен, Пол Microsoft SQL Server 2005 Біблія користувача : Пер з англ – М: ООО ІД Вільямс , 2008 – 1232 с : Ил – Парал тит англ

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*