在 SQL Server 中创建审核触发器
我需要对 SQL Server 2005 数据库中的两个表实施更改跟踪。我需要审核添加、删除、更新(详细说明更新内容)。我本来计划使用触发器
来执行此操作,但似乎很容易错误地执行此操作。
任何人都可以发布一个更新触发器的示例,以优雅的方式成功地完成此任务吗?我希望最终得到一个具有以下结构的审计表:
- ID
- LogDate
- TableName
- TransactionType (update/insert/delete)
- RecordID
- FieldName
- OldValue
- NewValue
...想法?
I need to implement change tracking on two tables in my SQL Server 2005 database. I need to audit additions, deletions, updates (with detail on what was updated). I was planning on using a trigger
to do this, but it seams that this is easy to do this incorrectly.
Can anybody post an example of an update trigger that accomplishes this successfully and in an elegant manner? I am hoping to end up with an audit table with the following structure:
- ID
- LogDate
- TableName
- TransactionType (update/insert/delete)
- RecordID
- FieldName
- OldValue
- NewValue
... thoughts?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
我只想指出几点:
使用代码生成器您无法使用单个过程来跟踪所有表,您需要在每个跟踪的表上生成类似但不同的触发器。这种工作最适合自动代码生成。在您的位置,我将使用 XSLT 转换从 XML 生成代码,并且可以从元数据自动生成 XML。这使您可以在每次更改审计逻辑/结构或添加/更改目标表时重新生成触发器,从而轻松维护触发器。
考虑审核容量规划。到目前为止,跟踪所有值更改的审计表将是数据库中最大的表:它将包含所有当前数据和
当前数据的所有历史记录。这样的表将使数据库大小增加 2-3 个数量级(x10、x100)。并且审计表将很快成为一切的瓶颈:
考虑到架构更改。可以删除名为“Foo”的表,然后可以创建名为“Foo”的不同表。审计跟踪必须能够区分两个不同的对象。最好使用缓慢变化维度方法。
考虑有效删除审核记录的需要。当您的应用程序主题策略规定的保留期到期时,您需要能够删除到期的审核记录。现在看来这似乎没什么大不了的,但 5 年后,当第一条记录到期时,审计表已增长到 9.5TB,这可能是一个问题。
考虑查询审核的需要。必须准备审计表结构以有效地响应审计查询。如果您的审计无法被查询,那么它就没有价值。查询将完全由您的需求驱动,并且只有您知道这些,但大多数审计记录都是按时间间隔(“昨天晚上 7 点到 8 点之间发生了什么变化?”)、按对象(“此记录在此记录中发生了什么变化”)进行查询。表?”)或按作者(“Bob 在数据库中做了哪些更改?”)。
I just want to call out couple of points:
Use code generators You can't have a single procedure to track all tables, you will need to generate similar but distinct triggers on each tracked table. This kind of job is best suited for automated code generation. In your place I would use an XSLT transformation to generate the code from XML, and the XML can be generated automatically from metadata. This allows you to easily maintain the triggers by regenerating them each time you make a change to the audit logic/structure or a target table is added/altered.
Consider capacity planning for the audit. An audit table that tracks all value changes will be, by far, the biggest table in the database: it will contain all the current data and
all the history of the current data. Such a table will increase the database size by 2-3 orders of magnitude (x10, x100). And the audit table will quickly become the bottleneck of everything:
Take into account the schema changes. A table named 'Foo' may be dropped and later a different table named 'Foo' may be created. The audit trail has to be able to distinguish the two different objects. Better use a slow changing dimension approach.
Consider the need to efficiently delete audit records. When the retention period dictated by your application subject policies is due, you need to be able to delete the due audit records. It may not seem such a big deal now, but 5 years later when the first records are due the audit table has grown to 9.5TB it may be a problem.
Consider the need to query the audit. The audit table structure has to be prepared to respond efficiently to the queries on audit. If your audit cannot be queried then it has no value. The queries will be entirely driven by your requirements and only you know those, but most audit records are queried for time intervals ('what changes occurred between 7pm and 8pm yesterday?'), by object ('what changes occurred to this record in this table?') or by author ('what changes did Bob in the database?').
我们使用 ApexSQL Audit 来生成审核触发器,下面是该工具使用的数据结构。如果您不打算购买第三方解决方案,您可以在试用模式下安装此工具,了解他们如何实现触发器和存储,然后为自己创建类似的东西。
我没有费心去了解这些表如何工作的太多细节,但希望这能让您开始。
We are using ApexSQL Audit that generates audit triggers and below are data structures used by this tool. If you don’t plan on buying a 3rd party solution you can install this tool in trial mode, see how they implemented triggers and storage and then create something similar for yourself.
I didn’t bother getting into too many details on how these tables work but hopefully this will get you started.
没有通用的方法可以按照您想要的方式进行操作。最终您最终会为每个表编写大量代码。更不用说如果您需要比较每一列的变化,它可能会非常慢。
此外,您可能同时更新多行这一事实意味着您需要打开一个游标来循环所有记录。
我的方法是使用与您正在跟踪的表结构相同的表,并稍后将其逆透视以显示哪些列实际发生了更改。我还会跟踪实际进行更改的会话。这假设您在被跟踪的表中有主键。
因此,给定一个这样的表,
我将在审计 schmea 中创建一个这样的审计表。
和这样的更新触发器
这运行得相当快。在报告期间,您只需根据 sessionID 和主键连接行并生成报告。或者,您可以使用批处理作业定期检查审核表中的所有表并准备显示更改的名称-值对。
华泰
There is no generic way to do it the way you want. Ultimately you end up writing reams of code for each table. Not to mention it can be fairy slow if you need to compare each column for change.
Also the fact that you might be updating multiple rows at the same time implies you need to open a cursor to loop through all the records.
The way I'd do it will be using table with structure identical to the tables you are tracking and unpivot it later to show which columns have actually changed. I'd also keep track of the session that actually did the change. This assumes that you have primary key in the table being tracked.
So given a table like this
I'd create an audit table like this in the audit schmea.
And a trigger for Update like this
This runs quite fast. During reporting , you simply join the rows based on sessionID, and Primary key and produce a report. Alternatively you can have a batch job that periodically goes through all the tables in the audit table and prepare a name-value pair showing the changes.
HTH
它看起来很简单,并且应该可以很好地工作,直到表中有 image/varbinary 等元素
您拥有完整的旧记录和全新的 xml 记录。
还应该可以正常工作以批量插入多列。
并触发代码
OUTPUT
It looks simple and should work well until you have image/varbinary etc elements in your tables
You have whole old record and whole new record as xml.
Should also work properly for inserting multiple columns at 1 batch.
And trigger code
OUTPUT
我终于找到了一个通用的解决方案,不需要动态sql并记录所有列的更改。
如果表发生变化,不需要更改触发器。
这是审核日志:
这是一个表的触发器:
该触发器适用于一个名为 AGB 的表。名为 AGB 的表有一个名为 ID 的主键列和一个名为 LastModifiedBy 的列,其中包含上次编辑的用户名。
触发器由两部分组成,首先它将插入和删除表的列转换为行。此处详细解释: https://stackoverflow.com/a/43799776/4160788
然后它连接行 (按主键和字段名称插入和删除的表的每列一行),并为每个更改的列记录一行。它不记录 ID、TimeStamp 或 LastModifiedByColumn 的更改。
您可以插入自己的表名、列名。
您还可以创建以下存储过程,然后调用该存储过程来生成触发器:
I finally found a universal solution, that does not require dynamic sql and logs changes of all columns.
Its not needed to change the trigger if the table changes.
This is the audit log:
This is the trigger for one table:
This trigger is for one Table named AGB. The Table with the name AGB has a primary Key Column with the name ID and a Column with the Name LastModifiedBy which contains the username that made the last edit.
The trigger consists of two parts, first it converts columns of inserted and deleted tables into rows. This is explained in detail here: https://stackoverflow.com/a/43799776/4160788
Then it joins the rows (one row per column) of the inserted and deleted tables by primary key and field name, and logs a line for each changed column. It does NOT log changes of ID, TimeStamp or LastModifiedByColumn.
You can insert your own TableName, Columns names.
You can also create the following stored procedure, and then call this stored procedure to generate your triggers:
每个想要监控的表都需要它自己的触发器。很明显,正如已接受的答案中所指出的那样,代码生成将是一件好事。
如果您喜欢这种方法,那么使用此触发器并用为每个表单独生成的代码替换一些通用步骤可能是一个主意。
尽管如此,我还是创建了一个完全通用的审核触发器。观察到的表必须有一个主键,但这个主键甚至可能是多列。
某些列类型(例如 BLOB)可能不起作用,但您可以轻松排除它们。
这不会是最好的性能:-D
说实话:这更像是一种练习...--
一些表来测试它(故意使用古怪的 PK 列...)
--一些测试数据
--这是当前的内容
--审计的触发器
--现在我们用一些操作来测试一下:
--检查最终状态
--清理(小心真实数据!)
插入的结果
更新的选择性结果
以及删除的结果
Each table one wants to monitor, will need its own trigger. It is pretty obvious, that - as pointed out in the accepted answer - code generation will be a good thing.
If you like this approach, it might be an idea to use this trigger and replace some generic steps with generated code for each table separately.
Nevertheless I created a fully generic Audit-Trigger. The observed table must have a PK, but this PK might even be multi-column.
Some column types (like BLOBs) might not work, but you could easily exclude them.
This will not be the best in performance :-D
To be honest: This is more kind of an exercise...
--Some table to test this (used quirky PK columns on purpose...)
--Some test data
--This is the current content
--The trigger for the audit
--Now let's test it with some operations:
--Check the final status
--Clean up (carefull with real data!)
The result of the insert
The selective result of an update
And the result of a delete
我有一个与我所使用的您提议的设计非常相似的表格。
我向选定的表添加了插入、更新和删除触发器,然后检查选定字段的更改。当时它很简单,而且效果很好。
以下是我使用这种方法发现的问题:
审计表旧/新值字段必须是 varchar(MAX) 类型才能处理可以审计的所有不同值:int、bool、decimal、float 、varchar 等都必须适合
检查每个字段的代码编写维护起来很乏味。也很容易错过一些事情(例如将 null 字段更改为未捕获的值,因为 NULL != value 是 NULL。
删除记录:如何记录这个?所有字段?选定的字段?它变得复杂
我未来的愿景是使用一些 SQL-CLR 代码并编写一个通用触发器,该触发器被执行并检查表元数据以查看要审核的内容。其次,新/旧值将转换为 XML 字段并记录整个对象:这会产生更多数据,但删除会产生完整记录。网络上有几篇关于 XML 审核触发器的文章。
I have a very similar table to your proposed design that I have used.
I added insert, update and delete triggers to selected tables, and then checked for changes to selected fields. At the time it was simple and works well.
Here are the issues I find with this approach:
The audit table old/new value fields had to be varchar(MAX) types to be able to handle all the different values that could be audited: int,bool,decimal,float,varchar, etc. all have to fit
The code to check for each field is tedious to write an maintain. It's also easy to miss things (like changing a null field to a value didn't get caught, because NULL != value is NULL.
Delete record: how do you record this? All fields? Selected ones? It gets complicated
My future vision is to use some SQL-CLR code and write a generic trigger that is executed and checks table meta-data to see what to audit. Secondly, the New/Old values will be converted to XML fields and the whole object recorded: this results in more data but a delete has a whole record. There are several articles on the web about XML audit triggers.
触发器用于如果您修改或插入特定表,它将执行,并且您可以检查触发器中的特定列。带有解释的完整示例位于以下网站中。
http://www.allinworld99.blogspot.com/2015/04 /triggers-in-sql.html
Trigger is used to If you modify or insert in a particular table this will execute, and you can check the particular column in trigger. Full Example with explanation is in the following website.
http://www.allinworld99.blogspot.com/2015/04/triggers-in-sql.html