我使用CTE检索数据库中任何表的审计记录。例如,我有以下表格:
CREATE TABLE [customers]
(
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [varchar](50) NULL
);
CREATE TABLE [customers_orders]
(
[id] [int] IDENTITY(1,1) NOT NULL,
[customer_id] [int] NOT NULL,
[date_time] [datetime] NULL DEFAULT (getdate())
);
CREATE TABLE [customers_orders_lines]
(
[id] [int] IDENTITY(1,1) NOT NULL,
[order_id] [int] NOT NULL,
[quantity] [int] NULL,
);
我的审计表是这样的:
CREATE TABLE [audit]
(
[id] [int] IDENTITY(1,1) NOT NULL,
[user_name] [varchar](50) NULL,
[date_time] [datetime] NULL DEFAULT (getdate()),
[parent_table] [varchar](50) NULL,
[parent_id] [int] NULL,
[table_name] [varchar](50) NULL,
[table_id] [int] NULL,
[action] [varchar](1) NULL
)
customers表的条目也将parent_table
和parent_id
列分别设置为table_name
和table_id
。动作可以具有插入,更新或删除的I,U,D值。
我的CTE是这样的:
DECLARE @tablename varchar(100)
SET @tablename = 'customers'
DECLARE @tableid int
SET @tableid = 100
;WITH cteAudit AS
(
SELECT id, [user_name], date_time, table_name, table_id, action, 1 AS audit_level
FROM audit
WHERE
table_name = @tablename AND
table_id = @tableid
UNION ALL
SELECT a.id, a.[user_name], a.date_time, a.table_name, a.table_id, a.action, cteAudit.audit_level + 1
FROM audit a
INNER JOIN cteAudit
ON a.parent_id = cteAudit.table_id
AND a.parent_table = cteAudit.table_name
WHERE
a.parent_table <> a.table_name AND
a.parent_id <> a.table_id
)
SELECT * FROM cteAudit ORDER BY date_time DESC, id_no, audit_level
所以,我添加了一个客户,他的订单和订单行。所有记录都是我的行动。 Audit cte正确检索记录。当我为具有操作U的客户添加1个审计记录时,返回的记录加倍。
cte应该返回客户和相关表的所有审计记录。
此SQLFiddle显示问题。
我在这里错过了什么吗?
答案 0 :(得分:1)
嗯,你有一个递归的CTE,seems to work as designed,它在获取锚行之后,然后递归到链接到更新行的所有子项。
由于cte已经通过audit_level
跟踪其深度,如果你需要将递归限制在没有子项的指定表中,只需过滤audit_level
:
SELECT * FROM cteAudit
WHERE AUDIT_LEVEL = 1
ORDER BY date_time DESC, audit_level
修改强>
您审核信息的方式的问题在于您有多个audit rows
(在您的小提琴示例中,插入和更新)通过实时表PK链接回相同的live
表参考数据,没有您引用的版本的上下文。
意思是随时添加另一个parent update
(正如您通过向客户添加新的'U'所做的那样),您的递归CTE将再次拉动(〜交叉连接)所有链接的子审计记录,而不管'当'他们被改变 - 如上所述,这似乎是审计模型和CTE的设计/意图,因为在数据模型的当前版本中,您无法重建父/子/孙子图的实际版本它被修改的时间。
要限制CTE仅提取更改时实际修改的数据的聚合根(customer
及其子customers_orders
和孙子customers_orders_lines
),将需要更改您的审核模式以包含version or timestamp
更改的数据。
然后,此版本需要作为新字段包含在审计表中,并且此版本需要包含在cte的连接键中(即table_name
,table_id
和{ {1}})。
答案 1 :(得分:0)
我不确定,但我认为在这种情况下您不需要UNION ALL
运算符。
<{1}}运算符剂量不会删除重复UNION ALL
将不允许在您的查询中。
Union
但我认为这不是为您的应用程序存储审计数据的最佳标准方法。
最好的方法是使用DECLARE @tablename varchar(100);
DECLARE @tableid int;
SELECT @tablename = 'customers', @tableid = 100;
;WITH cteAudit AS
(
SELECT id, [user_name], date_time, table_name, table_id, action, 1 AS audit_level
FROM audit
)
SELECT * FROM cteAudit
ORDER BY date_time DESC, audit_level;
Triggers to Audit your data.
您将获得一个表,其中存储您已完成的最后一个插入的信息。 你只需要创建触发器&amp;从插入的表中获取值&amp;插入审计表。
On Insert of new Row :
当某人删除您表格中的任何记录时,您就可以访问已删除表格。该表存储了上次删除的记录的信息。
On DElete of new Row :
您可以访问On Update of table :
和&amp;在这种情况下,inserted
表。
删除表由更新前的旧数据组成 插入的表由更新后的新数据组成
这是清除所有疑虑的最佳链接。关于触发器的基本概念&amp;审核数据。