【问题标题】:Check for changes to an SQL Server table?检查对 SQL Server 表的更改?
【发布时间】:2010-09-05 05:29:51
【问题描述】:

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

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

“更改表格”我的意思是更改表格数据,而不是更改表格结构。

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


考虑到我的要求(没有触发器或架构修改,SQL Server 2000 和 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);

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

【问题讨论】:

  • 他们没有碰巧在他们的行上加上最后修改的时间戳,是吗?
  • 作为记录,如果版本支持是 SQL Server 2005 或更高版本。我将看看 SQL Server 的 Service Broker 功能。

标签: sql sql-server datatable rdbms


【解决方案1】:

很遗憾,CHECKSUM 并不总能正常工作以检测更改

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

因此您不能使用它来检测所有更改,例如。 G。对称变化会产生相同的校验和!

E. G。 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!

【讨论】:

  • 这实际上不是答案,而是“你的建议不起作用”。
  • 这可以通过在 BINARY_CHECKSUM 之前使用 DISINCT 关键字来解决重复数据的问题。 here 还讨论了一些其他陷阱,但并不完全是常见的情况。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 请提供一个文档化的方式来获取此信息范围为 SQL Server 中的表
    【解决方案4】:

    看看 CHECKSUM 命令:

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

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

    CHECKSUM

    以下是我在表更改时使用它重建缓存依赖项的方法:
    ASP.NET 1.1 database cache dependency (without triggers)

    【讨论】:

    • 校验和最终会失败。如果您的系统接受两组不同的数据将产生相同的校验和,那么您就可以了。出于这个原因,我不得不放弃我们大多数系统中的校验和......
    • @LPains 你能详细说明你的陈述吗?
    • @petrosmm 我不确定您希望我具体说明什么,但我会尝试。想象一下,您有一个包含数百条记录的表,您基本上生成一个整数作为校验和,这会多久发生一次冲突?就我而言,我用大约 10 个表来做这件事,所有表都有数百条记录。我每天至少发生一次碰撞。检查这个其他答案stackoverflow.com/questions/14450415/…
    【解决方案5】:

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

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

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

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

    【讨论】:

      【解决方案6】:

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

      【讨论】:

        【解决方案7】:

        这里猜测一下:如果你不想修改第三方的表,你能不能创建一个视图然后在那个视图上放一个触发器?

        【讨论】:

          【解决方案8】:

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

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

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

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

          【讨论】:

          • 问题是关于表数据的变化,information_schema包含表的schema(列定义)。
          猜你喜欢
          • 2011-05-17
          • 2015-06-24
          • 1970-01-01
          • 2012-05-12
          • 1970-01-01
          • 1970-01-01
          • 2017-09-05
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多