欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SQL点滴24 监测表的变化

程序员文章站 2023-12-05 19:03:10
有时候,我们在某一重要的时间段需要监控某张表的变化情况,包含插入、更新、删除。举例来说,当我们把数据导出到外部的系统时,我们希望导出的是全部的数据,而且最好是导出上次导出之...
有时候,我们在某一重要的时间段需要监控某张表的变化情况,包含插入、更新、删除。举例来说,当我们把数据导出到外部的系统时,我们希望导出的是全部的数据,而且最好是导出上次导出之后变动的数据。
作为dba,我们可采传统的触发器操作,来构建一个元数据表或一个时间戳列来监控数据的变化。
代码如下:code listing 1
该代码在 sql 2005(sp3), sql 2008 r2 (rtm with cu5)测试通过
复制代码 代码如下:

-------------------
--method 1: trigger
-------------------
--base table definition
if object_id('checksumtest', 'u') is not null drop table checksumtest
go
create table checksumtest
(
id int identity(1,1) not null primary key,
vc1 varchar(1) not null,
vc2 varchar(1) not null
)
go
insert dbo.checksumtest (vc1, vc2) select 'a', 'b'
insert dbo.checksumtest (vc1, vc2) select 'b', 'a'
go
--create audit summary table to hold meta-data
if object_id('dbo.tableauditsummary', 'u') is not null drop table dbo.tableauditsummary
create table dbo.tableauditsummary
( id int identity(1,1) not null primary key,
tablename sysname not null,
lastupdate datetime not null,
lastexport datetime not null
)
go
insert dbo.tableauditsummary (tablename, lastupdate, lastexport) values ('dbo.checksumtest', getdate(), getdate())
go
--tables that need exporting
select * from dbo.tableauditsummary where lastupdate>lastexport
--create trigger on all base tables
--this fires on any insert/update/delete and writes new lastupdate column for the table set to current date and time
if object_id('dbo.trg_checksumtest_maintainauditsummary', 'tr') is not null drop trigger dbo.trg_checksumtest_maintainauditsummary
go
create trigger dbo.trg_checksumtest_maintainauditsummary
on dbo.checksumtest
after insert, update, delete
as
begin
if (object_id('dbo.checksumtest') is not null)
update dbo.tableauditsummary set lastupdate=getdate() where tablename='dbo.checksumtest'
end
go
--make an update
update dbo.checksumtest set vc1='b', vc2='a' where id=1
update dbo.checksumtest set vc1='a', vc2='b' where id=2
--check meta-data
select * from dbo.tableauditsummary where lastupdate>lastexport
--when we have exported the data, we run the following to reset metadata
update dbo.tableauditsummary set lastexport=getdate() where lastupdate>lastexport

最近我正在读关天sqlserver在线帮助(bol)相关的知识, 我接触到了 sql server checksum(), binary_checksum(), and checksum_agg() 这几个函数, 由此突然想到这些函数是不是也可以监控表的数据变化,而事实证明checksum_agg() 函数尽管被描述为检测表的变化,但这里不适用.
使用 checksum() and checksum_agg() 函数
checksum_agg() 函数, 在books online 和许多相关的站点上是这样描述的, 通常用于检测一个表的数据是否更改. 这是一个代替触发器的更好的方法,只是该操作会引起表扫描的操作。于是我这次我仍然使用元数据来跟踪数据的变化,只是新建了列lastchksum代替了lastupdate,该列用于保存checksum_agg(binary_checksum(*)),它将会在全表中产生一个唯一值,以区别数据的变化情况。
代码如下: listing 2.
复制代码 代码如下:

---------------------------------------------
--method 2 : using checksum (not reliable)
---------------------------------------------
--base table definition
if object_id('checksumtest', 'u') is not null drop table checksumtest
go
create table checksumtest
(
id int identity(1,1) not null primary key,
vc1 varchar(1) not null,
vc2 varchar(1) not null
)
go
insert dbo.checksumtest (vc1, vc2) select 'a', 'b'
insert dbo.checksumtest (vc1, vc2) select 'b', 'a'
go
--create audit summary table to hold meta-data
if object_id('dbo.tableauditsummary', 'u') is not null drop table dbo.tableauditsummary
create table dbo.tableauditsummary
( id int identity(1,1) not null primary key,
tablename sysname not null,
lastchksum int not null
)
go
insert dbo.tableauditsummary (tablename, lastchksum)
select 'dbo.checksumtest', checksum_agg(binary_checksum(*)) from dbo.checksumtest
go
--tables that need exporting
select * from dbo.tableauditsummary where tablename='dbo.checksumtest'
and lastchksum<>(select checksum_agg(binary_checksum(*)) from dbo.checksumtest)
union all
...
--make a simple (single row) update
update dbo.checksumtest set vc1='c', vc2='a' where id=1
--tables that need exporting
select * from dbo.tableauditsummary where tablename='dbo.checksumtest'
and lastchksum<>(select checksum_agg(binary_checksum(*)) from dbo.checksumtest)
union all
...
--reset metadata
update dbo.tableauditsummary set lastchksum=(select checksum_agg(binary_checksum(*)) from dbo.checksumtest)
where tablename='dbo.checksumtest'
--make a symmetric change
update dbo.checksumtest set vc1='b', vc2='a' where id=1
update dbo.checksumtest set vc1='c', vc2='a' where id=2
--tables that need exporting (no rows returned as checksum_agg() has not changed!!)
select * from dbo.tableauditsummary where tablename='dbo.checksumtest'
and lastchksum<>(select checksum_agg(binary_checksum(*)) from dbo.checksumtest)
union allcode listing 2

正如你所看到的那样,对于单个的变化的情况,checksum是使用比较好的,但是checksum_agg()却不能反应数据的变化
代码如下:code listing 3
复制代码 代码如下:

--base table definition
if object_id('checksumtest', 'u') is not null drop table checksumtest
go
create table checksumtest
(
id int identity(1,1) not null primary key,
vc1 varchar(1) not null,
vc2 varchar(1) not null,
chksum1 as (checksum(id, vc1, vc2)),
chksum2 as (binary_checksum(id, vc1, vc2))
)
go
insert dbo.checksumtest (vc1, vc2) select 'a', 'b'
insert dbo.checksumtest (vc1, vc2) select 'b', 'a'
go
--show computed columns and checksum_agg() value = 199555
select * from checksumtest
select checksum_agg(binary_checksum(*)) from checksumtest
--make a simple (single row) update
update dbo.checksumtest set vc1='c', vc2='a' where id=1
--show computed columns and checksum_agg() value = 204816 (ok)
select * from checksumtest
select checksum_agg(binary_checksum(*)) from checksumtest
--make a symmetric change
update dbo.checksumtest set vc1='b', vc2='a' where id=1
update dbo.checksumtest set vc1='c', vc2='a' where id=2
--show computed columns and checksum_agg() value = 204816 (not ok!)
select * from checksumtest
select checksum_agg(binary_checksum(*)) from checksumtest

我们会发现调整前后 checksum_agg(binary_checksum(*)) 的值是一样的,不能区分
结论:
checksum_agg() 函数尽管被描述为能监测表数据的变化,在实际测试中是不行的。尤其是对表进行对称数据修改时,无法监测
作者:tyler ning