T-SQL:在修改事务结束前运行的触发器
T-SQL: Trigger that runs right before the end of a modifying transaction
问题陈述
我有一个视图可以递归地收集和聚合来自 3 个不同的大到非常大的 table 的信息。这个视图本身需要相当长的时间来执行,但在许多 select 语句中都需要并且经常执行。
然而,生成的视图非常小(2 列中有几十个结果)。
所有更新操作通常会启动一个事务,执行数千个 INSERT,然后提交事务。这种情况不会经常发生,但如果有东西写入数据库,通常是大量数据。
我试过的
- 由于视图很小,不经常更改并且经常被阅读,所以我想到了创建一个索引视图。但是,很遗憾,您无法使用 CTE 甚至递归 CTE 创建索引视图。
- 为了 'emulate' 索引视图或物化视图,我考虑编写一个触发器来执行视图并将结果存储到 table 中,每次基 tables得到修改。但是,我想如果大量条目被更新或插入并且触发器为这些 table 上的每个 INSERT/UPDATE 语句运行,即使它们在单个事务中,我想这将永远持续下去。
真题
是否可以编写一个触发器,在提交之前和事务的最后一个 insert/update 语句完成后运行 一次,并且仅当任何语句已完成时更改了三个 tables?
中的任何一个
不,没有直接的方法可以在交易结束前 运行 触发。 DML 触发器 运行 每个触发 DML 语句一次(INSERT
、UPDATE
、DELETE
),并且没有其他类型的与数据修改相关的触发器。
间接地,您可以将所有 INSERT 插入到临时 table,然后将它们从 #temp table 一起插入到真实 table,从而得到一个table 触发触发。但是如果你正在写入多个 tables,你仍然会遇到同样的问题。
解决此问题的 SOP(标准操作规范)方法是让存储过程预先处理所有内容,而不是让触发器在后面尝试捕获所有内容。
如果数据一致性很重要,那么我建议您遵循我上面提到的基于存储过程的 SOP 方法。这是此方法的高级概述:
- 首先使用将所有更改转储到#temp tables 的存储过程,
- 然后开始交易,
- 然后进行更改,将 data/changes 从您的#temp table(s) 移动到实际的 tables,
- 然后在触发器中执行您想要的后续工作。如果这些是一致性检查,那么如果它们失败了,您应该回滚事务。
- 否则,它会提交事务。
这几乎总是正确完成此类操作的方式。
如果您的视图较小且查询频繁,并且您的下划线 table 很少更改,则不需要 "view"。相反,您需要一个摘要 table,该摘要具有与视图相同的结果,并由您的触发器在每个下划线 table.
上更新
每次数据修改(插入、删除、更新)都会触发一个触发器,但是一次修改只会触发一次,无论是更新一条记录还是更新一百万行。您无需担心更新的大小。相反,更新的频率是您关心的问题。
如果您有一个程序定期插入大量行,或逐行更新大量行,您可以更改程序并在更新前禁用触发器,以便更新摘要 table仅在过程结束之前,您可以在其中调用相同的 "sum" 过程并启用这些触发器。
如果您必须始终保持 "summary" 最新,即使在大量交易期间(我怀疑它是否非常有用或实用,如果您的视图执行缓慢),您可以禁用这些触发器,在每次交易后自己进行一些计算,在每次交易后更新摘要 table,在您的程序中。
问题陈述
我有一个视图可以递归地收集和聚合来自 3 个不同的大到非常大的 table 的信息。这个视图本身需要相当长的时间来执行,但在许多 select 语句中都需要并且经常执行。
然而,生成的视图非常小(2 列中有几十个结果)。
所有更新操作通常会启动一个事务,执行数千个 INSERT,然后提交事务。这种情况不会经常发生,但如果有东西写入数据库,通常是大量数据。
我试过的
- 由于视图很小,不经常更改并且经常被阅读,所以我想到了创建一个索引视图。但是,很遗憾,您无法使用 CTE 甚至递归 CTE 创建索引视图。
- 为了 'emulate' 索引视图或物化视图,我考虑编写一个触发器来执行视图并将结果存储到 table 中,每次基 tables得到修改。但是,我想如果大量条目被更新或插入并且触发器为这些 table 上的每个 INSERT/UPDATE 语句运行,即使它们在单个事务中,我想这将永远持续下去。
真题
是否可以编写一个触发器,在提交之前和事务的最后一个 insert/update 语句完成后运行 一次,并且仅当任何语句已完成时更改了三个 tables?
中的任何一个不,没有直接的方法可以在交易结束前 运行 触发。 DML 触发器 运行 每个触发 DML 语句一次(INSERT
、UPDATE
、DELETE
),并且没有其他类型的与数据修改相关的触发器。
间接地,您可以将所有 INSERT 插入到临时 table,然后将它们从 #temp table 一起插入到真实 table,从而得到一个table 触发触发。但是如果你正在写入多个 tables,你仍然会遇到同样的问题。
解决此问题的 SOP(标准操作规范)方法是让存储过程预先处理所有内容,而不是让触发器在后面尝试捕获所有内容。
如果数据一致性很重要,那么我建议您遵循我上面提到的基于存储过程的 SOP 方法。这是此方法的高级概述:
- 首先使用将所有更改转储到#temp tables 的存储过程,
- 然后开始交易,
- 然后进行更改,将 data/changes 从您的#temp table(s) 移动到实际的 tables,
- 然后在触发器中执行您想要的后续工作。如果这些是一致性检查,那么如果它们失败了,您应该回滚事务。
- 否则,它会提交事务。
这几乎总是正确完成此类操作的方式。
如果您的视图较小且查询频繁,并且您的下划线 table 很少更改,则不需要 "view"。相反,您需要一个摘要 table,该摘要具有与视图相同的结果,并由您的触发器在每个下划线 table.
上更新每次数据修改(插入、删除、更新)都会触发一个触发器,但是一次修改只会触发一次,无论是更新一条记录还是更新一百万行。您无需担心更新的大小。相反,更新的频率是您关心的问题。
如果您有一个程序定期插入大量行,或逐行更新大量行,您可以更改程序并在更新前禁用触发器,以便更新摘要 table仅在过程结束之前,您可以在其中调用相同的 "sum" 过程并启用这些触发器。
如果您必须始终保持 "summary" 最新,即使在大量交易期间(我怀疑它是否非常有用或实用,如果您的视图执行缓慢),您可以禁用这些触发器,在每次交易后自己进行一些计算,在每次交易后更新摘要 table,在您的程序中。