Audit data using SQL Server 2005's COLUMNS_UPDATED function

Audit data using SQL Server 2005's COLUMNS_UPDATED function

By Tim Chapman, TechRepublic | 2008/09/18 11:38:01

Tags: function, sql server 2005

 

Auditing data changes in your production environment is very important, especially if you are dealing with confidential information.

Besides tracking the data that has been modified, it is also useful to track the individual field names that have been modified. This information is great for your auditing department, and it is very useful when debugging your database code. SQL Server 2005 provides two functions for tracking which fields have been modified: UPDATE and COLUMNS_UPDATED.

UPDATE

This TSQL trigger function accepts a field name and returns a Boolean value indicating if the specified field has been included in the update statement that invoked the trigger. This function will always return a true value when an Insert statement is invoked. The form of usage for this function is:

IF UPDATE(FieldName)
BEGIN --work to do
END

This function works great if you are specifically only looking for actions to be taken when certain fields are updated. But, what if you want to record which fields were included in your update statement? It would be very difficult to write code that would determine the set of fields included in your update statement using only the UPDATE function. The COLUMNS_UPDATED function has been provided for just such a purpose.

COLUMNS_UPDATED

This TSQL function, which is only available through triggers, returns a binary mask of the fields that were included in your update statement based upon the column order of the table. However, writing code that takes a binary value and parses out the individual bits is tricky. A system function has been included in SQL Server 2005 that will make your job much easier. First, I'll look at how the COLUMNS_UPDATED function works, and then I'll discuss how to parse out the field names.

The code below shows how to create a table and add a record to it.

IF OBJECT_ID('SalesHistory','U') > 0
DROP TABLE SalesHistory
GO
CREATE TABLE SalesHistory
(
SaleID INT IDENTITY(1,1),
Product VARCHAR(30) NOT NULL,
SaleDate DATETIME,
SalePrice MONEY
)
GO

INSERT INTO SalesHistory ( Product, SaleDate, SalePrice)
VALUES
('Computer', DATEADD(mm, 55, '3/11/1919'), DATEPART(ms, GETDATE()) + (57) )
GO

The code below creates the audit table, which I will use to house the data changes.

IF OBJECT_ID('SalesHistoryAudit','U') > 0
DROP TABLE SalesHistoryAudit
GO
CREATE TABLE SalesHistoryAudit
(
AuditID INT IDENTITY(1,1),
SaleID INT,
Product VARCHAR(30) NOT NULL,
SaleDate DATETIME,
SalePrice MONEY,
ColumnsUpdated XML NULL
)
GO

The code below creates the trigger on your SalesHistory table.

CREATE TRIGGER tr_SalesHistory ON SalesHistory
FOR UPDATE
AS
BEGIN
DECLARE @FldsUpdated XML
DECLARE @ColumnsUpdated VARBINARY(100)

SET @ColumnsUpdated = COLUMNS_UPDATED()

SET @FldsUpdated =
(
SELECT COLUMN_NAME AS Name
FROM TRS.INFORMATION_SCHEMA.COLUMNS Field
WHERE
TABLE_NAME = 'SalesHistory' AND
sys.fn_IsBitSetInBitmask
(
@ColumnsUpdated,
COLUMNPROPERTY(OBJECT_ID(TABLE_SCHEMA + '.' + TABLE_NAME), COLUMN_NAME, 'ColumnID')
) <> 0
FOR XML AUTO, ROOT('Fields')
)

INSERT INTO SalesHistoryAudit
(SaleID, Product, SaleDate, SalePrice, ColumnsUpdated)
SELECT SaleID, Product, SaleDate, SalePrice, @FldsUpdated
FROM INSERTED
END
GO

The real power of this trigger is the sys.fn_IsBitSetInBitmask function. It accepts a bitmask value (which is the bitmask returned from our COLUMNS_UPDATED function) and the columnid from the INFORMATION_SCHEMA view and returns a non-zero value for each field in the table that is contained in the bitmask. Our trigger then casts the field names that were included in the update statement as an XML document so that we can store which fields were updated in our audit table.

Now that our trigger is in place, let's run an update statement that updates a value in our table. We can then check our audit table to see if we caught the fields from our update statement.

UPDATE TOP(1) SalesHistory
SET SalePrice = SalePrice + 1
GO
SELECT * FROM SalesHistoryAudit

Note to SQL Server 2000 users

The sys.fn_IsBitSetInBitmask is new to SQL Server 2005, so it isn't available in SQL Server 2000. However, you can copy the script from SQL Server 2005 and create your own function in SQL Server 2000. This will allow you to use this custom auditing solution on SQL Server 2000 machines.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值