检查对 SQLServer 表的更改?

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

我希望能够支持任何 SQLServer2000 SP4或更新。我的申请是另一家公司产品的数据可视化。我们的客户基础数以千计,所以我不希望在每次安装时都要提出修改第三方供应商表的要求。

“桌子的变化”指的是对表数据的更改,而不是对表结构的更改。

最终,我希望这个更改在我的应用程序中触发一个事件,而不是必须每隔一段时间检查更改。


考虑到我的需求(没有触发器或模式修改,SQLServer2000和2005) ,最好的办法似乎是在 T-SQL中使用 BINARY_CHECKSUM函数。我的计划是这样的:

每隔 X 秒运行以下查询:

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

然后将其与存储值进行比较。如果值已更改,则使用查询逐行遍历表:

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

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

73247 次浏览

你为什么不想用触发器?如果你正确地使用它们,它们是一件好事。如果你把它们作为一种执行参照完整性的方式,那么它们就会从好变坏。但是如果您使用它们进行监视,那么它们实际上并不被认为是禁忌。

有一个在给定时间间隔内运行的 DTS 作业(或由 Windows 服务启动的作业)。每次运行它时,它通过使用系统 信息 _ 方案表获取关于给定表的信息,并将这些数据记录到数据存储库中。将返回的有关表结构的数据与上次返回的数据进行比较。如果它是不同的,那么你知道结构已经改变。

返回表 ABC 中所有列的信息的示例查询(理想情况下只列出所需的 INFORATION _ SCHEMA 表中的列,而不是像我在这里所做的那样使用 * select * *) :

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

根据定义“对表的更改”的确切方式,可以监视不同的列和 INFORATION _ SCHEMA 视图。

看看 CHECKSUM 命令:

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

只要表内容没有更改,每次运行时返回相同的数字。更多信息请看我的帖子:

CHECKSUM

下面是我如何使用它在表发生变化时重新构建缓存依赖项:
NET 1.1数据库缓存依赖项(没有触发器)

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

你有没有考虑过换个角度看这件事?如果您不想修改模式以添加触发器(这是有道理的,因为它不是您的数据库) ,您是否考虑过与制造数据库的应用程序供应商合作?

他们可以实现一种 API,提供一种机制,通知附属应用程序数据已经更改。它可以很简单地写入一个通知表,列出修改了哪个表和哪个行。可以通过触发器或应用程序代码实现的。从你的角度来看,时间并不重要,你唯一关心的是定期扫描通知表。对数据库的性能影响将远远小于扫描每一行的更改。

最困难的部分是说服应用程序供应商实现这个特性。由于这可以通过触发器完全通过 SQL 进行处理,因此您可以通过编写和测试触发器,然后将代码交给应用程序供应商来完成大部分工作。通过让供应商支持触发器,可以防止添加触发器不经意地替换供应商提供的触发器的情况。

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

不幸的是,我认为在 SQL2000中没有一种干净的方法可以做到这一点。如果您将需求缩小到 SQLServer2005(以及更高版本) ,那么您就成功了。您可以在 System.Data.SqlClient中使用 SQLDependency类。参见 SQLServer 中的查询通知(ADO.NET)

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

它只是一个基本校验和,没有循环冗余校验(CRC)计算。

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

例如,使用 CHECKSUM_AGG(BINARY_CHECKSUM(*))的解决方案总是为所有3个内容不同的表提供0:


选择 CHECKSUM _ AGG (BINARY _ CHECKSUM (*)) FROM
(
选择1作为 numA,1作为 numB
工会
选择1作为 numA,1作为 numB
) q
送零分!

选择 CHECKSUM _ AGG (BINARY _ CHECKSUM (*)) FROM ( 选择1作为 numA,2作为 numB 工会 选择1作为 numA,2作为 numB ) q 送零分!

选择 CHECKSUM _ AGG (BINARY _ CHECKSUM (*)) FROM ( 选择0作为 numA,0作为 numB 工会 选择0作为 numA,0作为 numB ) q 送零分!

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