2014-11-02 62 views
2

我使用CTE检索我的数据库中的任何表的审计记录。例如,我有如下表: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 
) 

为客户表的条目也将有parent_tableparent_id列分别设置为table_nametable_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

请问您能分享一下与此cte相关的其他表结构吗? – 2014-11-02 08:04:07

回答

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 

SqlFiddle here

编辑
您审核信息的方式的问题在于您有多个audit rows(在您的小提琴示例中,插入和更新)链接回相同的live表格参考数据通过活动表PK,没有您引用哪个版本的上下文。

含义任何时候你添加其他parent update,你的递归CTE将再次拉(〜交叉连接),全挂子审计记录,(因为你已经加入了新的“U”以客户)的“时”,他们不管如上所述,这似乎是审计模型和CTE的设计/意图,因为在数据模型的当前版本中,您无法在其时重建父/子/孙图的实际版本被修改了。

要限制CTE拉聚合根(customer及其子customers_orders和孙子customers_orders_lines),这实际上是在改变的时候修改的只是数据,你需要改变你的审计模式包括version or timestamp的变化数据。

该版本将随后需要包括对审计表作为一个新的领域,这个版本将需要包含在CTE的连接键(即table_nametable_idrecord_version)。

+0

我使用SqlFiddle示例编辑了我的问题。它显示了问题。 – 2014-11-02 09:13:35

+0

添加版本是不可能的,因为我们的审计表可以追溯到10年以上。审计表已经有了一个date_time列,但是我不知道它是否会起作用,因为插入记录时可能会有几秒/毫秒的细微差异。 – 2014-11-03 07:32:44

0

我不确定,但我认为在这种情况下您不需要UNION ALL运算符。 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; 

但我认为这不是存储您的应用程序审计数据的最佳n标准方式。

最好的办法是使用Triggers to Audit your data.

On Insert of new Row : 

你会得到一个表,该表存储你已经完成了最后插入的信息。 你只需要创建触发器&从插入的表&插入到您的审计表中的值。

On DElete of new Row : 

当有人删除从表中的任何记录,那么你将可以访问删除表。该表存储最后删除的记录的信息。

On Update of table : 

你将能够访问在此情况下,两个inserted & deleted表。

删除表包含旧数据前updatation 插入表更新用

后由新的数据这是清除所有的疑虑&有关触发器的基本概念&审计数据的最佳链接。

Click Here For Triggers Information

+0

我已经在使用CLR触发器,以便相应地更新审计表。此cte将检索审计记录。 – 2014-11-02 10:07:39

+0

好吧您是否使用我的上述查询进行了检查。 – 2014-11-02 12:31:46