SQL 在更新、插入、删除非特定行、列或 table 时触发

SQL Trigger on Update, Insert, Delete on non-specific row, column, or table

我有几个数据库供多个应用程序使用(其中一个是我们自己的,其他的我们无法控制它们的作用)。

Out 软件必须知道数据库最后一次更改的时间。出于一些原因,我不想长篇大论,我们决定为每个具有单一字段的数据库使用一个新的 table:具有 GetDate() 作为值的 last_changed_on。通过这种方式,我们自己的软件可以检查上次更改的时间,并检查它为所述数据库存储的日期,如果日期比内存中存储的日期更新,则执行操作。

经过一些研究后,我们决定使用 触发器 是可行的方法,但根据我在网上找到的内容,触发器会查看您为更新设置的特定列。

我想知道的是,是否有一种方法可以使该过程自动化,或者只要有任何事情发生时就会触发明智地插入、更新、删除

所以我正在寻找这样的东西:

CREATE TRIGGER LastModifiedTrigger
ON [dbo].[anytable]
AFTER INSERT, UPDATE, DELETE
AS

   INSERT INTO dbo.LastModifiedTable (last_modified_on) VALUES (CURRENT_TIMESTAMP)

我知道上面的例子不是一个正确的触发条件,我对他们还很陌生,所以我不确定如何措辞。 有趣的是,我可以拥有自己的软件 运行 多个查询,自动为每个 table 和每一列创建查询,但我宁愿避免这样做,因为要跟踪所有这些触发器将是一个长期的痛苦 运行。 我希望每个数据库尽可能有一些触发器,如果​​只是不必为每个单独的列名创建触发器的话。

编辑:澄清一下:我试图避免必须创建一个自动脚本去扫描每个 table,并按顺序扫描每个 table 的每一列,以创建一个触发器以查看那里是否发生了某些更改。我目前最大的问题是 updates 上的触发行为,但我希望避免为 insertdelete[=69 指定 tables =]

编辑 2:为了避免将来造成混淆,我正在为 SQL 服务器(MS SQL/T SQL)和MySQL

编辑 3:事实证明我非常错误地阅读了文档并且(至少在 MySql 上)触发器在任何给定的更新列上激活而无需定义一个特定的。无论如何,我仍然想知道是否有一种方法可以比数据库中每个 table 拥有一个触发器更少。 (即 1 代表任何类型的 update(),1 代表任何类型的 insert(),1 代表任何类型的 delete()

编辑 4:忘记了覆盖 1 个字段的参数会带来性能问题,我已经考虑过这一点,现在我正在处理多行。我还通过我的软件代码为每个数据库处理了 3 个触发器(insert()update()delete())的创建,我真的希望这可以避免,但它不能。

解决方案

在 Internet 上进行了更多挖掘并不断发现与我正在寻找的结果相反的结果,以及大量的反复试验之后,我找到了解决方案。 首先也是最重要的:让触发器不依赖于 table(又名,触发器激活每个 table 是不可能的,它不能完成,这太糟糕,如果将其保留在程序代码之外会很好,但我对此无能为力。

其次:关于不特定于列的更新问题是一个错误,因为我搜索触发器的部分不依赖于特定列,只为我提供了触发器的示例。 以下解决方案适用于 MySql,我尚未在 SQL 服务器上对此进行测试,但我希望它不会有太大差异。

CREATE TRIGGER [tablename]_last_modified_insert
   AFTER INSERT/UPDATE/DELETE ON [db].[tablename]
     FOR EACH ROW
     BEGIN
        INSERT INTO [db].last_modified(last_modified_on)
        VALUES(current_timestamp())
     END

至于动态创建这些触发器,下面展示了我是如何让它工作的: 第一个查询:

SHOW TABLES

我运行上面的查询得到数据库中所有的table,排除我自己做的last_modified,循环遍历所有的,创建3个触发器每个。

非常感谢 Arvo 和 T2PS 的回复,他们的评论帮助我指明了正确的方向并编写了解决方案。

您假设 SQL 服务器触发器是按列的; CREATE TRIGGER 语法将触发器绑定到指定操作的命名 table。将在范围 (inserted & deleted) 中使用两个逻辑 table 调用触发器,其中包含由导致触发器触发的操作修改的行;如果您想检查特定列的值或更改,则触发器逻辑需要针对那些逻辑 tables.

进行操作

如果采用这种方法,则需要为每个希望以这种方式监控的 table 创建一个触发器;我们有类似的跟踪更改的需求(在更细粒度的级别),我们没有找到对应于 schema/database 中所有 table 的 "pseudotable"。您还应该知道锁定语义将通过这样做发挥作用,因为您将拥有来自多个 table 的触发器,所有触发器都针对同一行进行更新,作为单独操作的一部分——取决于并发模型实际上,如果您希望多个 DML 查询同时对您的数据库进行操作,您可能会通过这样做来查看性能后果。

我建议改为检查 Arvo 在上面的评论 link 是否适用;查询系统视图更有可能避免在您的方案中使用触发器引起的争用(和其他与性能相关的)问题。

在 Internet 上进行了更多挖掘并不断发现与我正在寻找的结果相反的结果,以及大量的反复试验之后,我找到了解决方案。 首先也是最重要的:让触发器不依赖于 table(也就是说,触发器激活每个 table 是不可能的,它不能完成,这太糟糕,如果将其保留在程序代码之外会很好,但我对此无能为力。

其次:关于不特定于列的更新问题是一个错误,因为我搜索触发器的部分不依赖于特定列,只为我提供了触发器的示例。 以下解决方案适用于 MySQL,我尚未在 SQL 服务器上对此进行测试,但我希望它不会有太大差异。

CREATE TRIGGER [tablename]_last_modified_insert
   AFTER INSERT/UPDATE/DELETE ON [db].[tablename]
     FOR EACH ROW
     BEGIN
        INSERT INTO [db].last_modified(last_modified_on)
        VALUES(current_timestamp())
     END

至于动态创建这些触发器,下面展示了我是如何让它工作的: 第一个查询:

SHOW TABLES

我运行上面的查询得到数据库中所有的table,排除我自己做的last_modified,循环遍历所有的,创建3个触发器每个。

也许您可以对 SQL 服务器使用审核:

CREATE SERVER AUDIT [ServerAuditName]
TO FILE
(
   FILEPATH = N'C:\Program Files......'
)
ALTER SERVER AUDIT [ServerAuditName] WITH (STATE=ON)
GO   
CREATE DATABASE AUDIT SPECIFICATION [mySpec]
FOR SERVER AUDIT [ServerAuditName]
ADD (INSERT, UPDATE, DELETE ON DATABASE::databasename BY [public])
WITH (STATE=ON)
GO

然后可以查询修改:

SELECT * 
FROM sys.fn_get_audit_file ('C:\Program Files......',default,default);
GO