150

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

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

对表的更改”是指对表数据的更改,而不是对表结构的更改。

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


考虑到我的要求(没有触发器或架构修改,SQL Server 2000 和 2005),最好的做法似乎是使用T-SQLBINARY_CHECKSUM中的函数。我计划实施的方式是这样的:

每 X 秒运行一次以下查询:

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

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

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

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

4

8 回答 8

100

看一下 CHECKSUM 命令:

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

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

校验和

以下是我在表更改时使用它重建缓存依赖项的方法:
ASP.NET 1.1 数据库缓存依赖项(无触发器)

于 2008-08-02T05:20:22.397 回答
30

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

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

因此你不能用它来检测所有的变化,例如对称变化导致相同的校验和!

例如。解决方案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!

于 2011-03-30T12:07:06.750 回答
24

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

于 2008-08-01T13:07:52.810 回答
20

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

您是否考虑过从不同的角度来解决这个问题?如果您不想修改架构以添加触发器(这很有意义,这不是您的数据库),您是否考虑过与创建数据库的应用程序供应商合作?

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

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

于 2008-08-03T13:59:24.063 回答
18

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

于 2008-08-06T01:54:21.687 回答
16

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

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

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

您将监控不同的列和 INFORMATION_SCHEMA 视图,具体取决于您如何准确定义“对表的更改”。

于 2008-08-01T14:06:28.560 回答
13

在这里疯狂猜测:如果您不想修改第三方的表格,您可以创建一个视图然后在该视图上放置触发器吗?

于 2008-08-05T01:12:37.527 回答
7

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

于 2014-07-24T04:58:25.450 回答