检查对SQL Server表的更改?


142

如何在不使用触发器或不以任何方式修改数据库结构的情况下监视SQL Server数据库中对表的更改?我首选的编程环境是.NET和C#。

我希望能够支持任何SQL Server 2000 SP4或更高版本。我的应用程序是另一家公司产品的附加数据可视化。我们的客户群有成千上万,因此我不想在每次安装时都提出修改第三方供应商表的要求。

通过“改变到餐桌”我平均变化表数据,而不是更改为表结构。

最终,我希望更改能够触发我的应用程序中的事件,而不是必须间隔检查更改。


根据我的要求(不进行触发器或架构修改,SQL Server 2000和2005),最好的操作方案似乎是BINARY_CHECKSUMT-SQL中使用该函数。我计划实现的方式是这样的:

每X秒运行以下查询:

SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*))
FROM sample_table
WITH (NOLOCK);

并将其与储值进行比较。如果值已更改,请使用查询逐行浏览表:

SELECT row_id, BINARY_CHECKSUM(*)
FROM sample_table
WITH (NOLOCK);

并将返回的校验和与存储的值进行比较。


3
他们没有碰巧在行上添加最后修改的时间戳,对吗?
zmbq 2011年

作为记录,如果版本支持是SQL Server 2005或更高版本。我将看一下SQL Server的Service Broker功能。
Marco Guignard

Answers:


97

看一下CHECKSUM命令:

SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM sample_table WITH (NOLOCK);

只要表内容未更改,每次运行时它将返回相同的数字。有关更多信息,请参阅我的帖子:

CHECKKSUM

当表发生更改时,我使用它来重建缓存依赖关系的方法如下:
ASP.NET 1.1数据库缓存依赖关系(无触发器)


2
校验和最终可能并且将失败。如果您的系统接受两组不同的数据将导致相同的校验和,那很好。因此,在大多数系统中,我不得不放弃校验和……
LPains

@LPains您可以详细说明一下吗?
petrosmm '18

1
@petrosmm我不确定您要我具体说些什么,但我会尽力的。假设您有一个包含几百条记录的表,实际上是生成一个整数作为校验和,那么相撞的频率是多少?以我为例,我用大约10个表来做,所有表都有数百条记录。我每天至少发生一次碰撞。检查此其他答案stackoverflow.com/questions/14450415/…–
LPains

29

不幸的是,CHECKSUM并不总是能够正常工作以检测更改

它只是原始校验和,没有循环冗余校验(CRC)计算。

因此,您不能使用它来检测所有变化,例如,对称变化会导致相同的CHECKSUM!

例如 该解决方案CHECKSUM_AGG(BINARY_CHECKSUM(*))将始终为具有不同内容的所有3个表提供0:


SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM 
(
  SELECT 1 as numA, 1 as numB
  UNION ALL
  SELECT 1 as numA, 1 as numB
)  q
-- delivers 0!

SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM ( SELECT 1 as numA, 2 as numB UNION ALL SELECT 1 as numA, 2 as numB ) q -- delivers 0!

SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM ( SELECT 0 as numA, 0 as numB UNION ALL SELECT 0 as numA, 0 as numB ) q -- delivers 0!


5
这实际上不是答案,而是“您的建议不起作用”。
kristianp '16

1
可以通过在BINARY_CHECKSUM之前使用DISINCT关键字来纠正重复的数据。这里讨论了其他一些陷阱但并非完全是常见的情况。
pblack

25

您为什么不想使用触发器?如果正确使用它们,则是一件好事。如果您将它们用作加强参照完整性的一种方式,那就是当它们从好到坏时。但是,如果将它们用于监视,则它们并不是真正的禁忌。


20

您需要多久检查一次更改以及数据库中的表有多大(就行大小而言)?如果使用CHECKSUM_AGG(BINARY_CHECKSUM(*))John建议的方法,它将扫描指定表的每一行。的NOLOCK提示会有所帮助,但是在大型数据库上,您仍然需要查找每一行。您还需要为每一行存储校验和,以便告诉您已更改。

您是否考虑过从另一个角度出发?如果您不想修改架构以添加触发器(从某种意义上说,这不是您的数据库),您是否考虑过与制作数据库的应用程序供应商合作?

他们可以实现一种API,该API提供了一种机制来通知附件应用程序数据已更改。就像写一个通知表列出了被修改的表和行一样简单。这可以通过触发器或应用程序代码来实现。从您的角度来看,这无关紧要,您唯一关心的就是定期扫描通知表。对数据库的性能影响远远小于扫描每一行的更改。

困难的部分是说服应用程序供应商实现此功能。由于这可以完全通过触发器的SQL处理,因此您可以通过编写和测试触发器并将代码带给应用程序供应商来为它们完成大部分工作。通过让供应商支持触发器,可以防止您添加触发器而无意间替换了供应商提供的触发器的情况。


18

不幸的是,我认为在SQL2000中没有一种干净的方法可以做到这一点。如果您将需求范围缩小到SQL Server 2005(及更高版本),那么您就可以开展业务。您可以在中使用SQLDependency该类System.Data.SqlClient。请参阅SQL Server(ADO.NET)中的查询通知


16

具有以给定间隔运行的DTS作业(或由Windows服务启动的作业)。每次运行时,它都使用系统INFORMATION_SCHEMA表获取有关给定表的信息,并将此数据记录在数据存储库中。将与表结构有关的返回数据与上次返回的数据进行比较。如果不同,则说明结构已更改。

查询示例以返回有关表ABC中所有列的信息(理想情况下仅列出所需的INFORMATION_SCHEMA表中的列,而不是像我在此处那样使用* select **):

select * from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = 'ABC'

您将根据定义“对表的更改”的精确程度来监视不同的列和INFORMATION_SCHEMA视图。


2
问题是关于表数据的更改,而information_schema包含表的架构(列定义)。
也是

13

在这里大胆猜测:如果您不想修改第三方的表,是否可以创建视图,然后在该视图上放置触发器?


6

检查最后提交日期。每个数据库都有每次提交的历史记录。我相信这是ACID合规性的标准。


1
请提供一种文档化的方式来使此信息的范围仅限于SQL Server中的表格
Martin Smith,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.