2012-09-05 32 views
0

使用插入和刪除表的一個警告是它們都可以是空的。還有其他的catchya我應該知道嗎?例如,插入的表可以包含新記錄以及更新的記錄嗎?可以插入和刪除混合操作中的數據嗎?

我靠這個邏輯來檢測觸發動作:

IF EXISTS(SELECT * FROM inserted) AND EXISTS(SELECT * FROM deleted) SET @operation = 'U' 
IF EXISTS(SELECT * FROM inserted) AND NOT EXISTS(SELECT * FROM deleted) SET @operation = 'I' 
IF NOT EXISTS(SELECT * FROM inserted) AND EXISTS(SELECT * FROM deleted) SET @operation = 'D' 
IF NOT EXISTS(SELECT * FROM inserted) AND NOT EXISTS(SELECT * FROM deleted) SET @operation = 'X' 

編輯:

這是我解決了審計跟蹤問題。它已經在插入,僞造更新,實際更新和刪除的MERGE語句中進行了測試。

ALTER TRIGGER [dbo].[LogInsertEditDelete] 
ON [dbo].[<TableToAudit>] 
    AFTER INSERT,DELETE,UPDATE 
AS 
BEGIN 
-- SET NOCOUNT ON added to prevent extra result sets from 
-- interfering with SELECT statements. 
SET NOCOUNT ON; 

--You will need to change @table to match the table to be audited 
DECLARE @table VARCHAR(50) 
SELECT @table = '<TableToAudit>' 

-- date and user 
DECLARE @updatedBy VARCHAR(50), 
     @timestamp DateTime 
SELECT @updatedBy = SYSTEM_USER, 
     @timestamp = GETDATE() 

-- Action, U = update, I = insert, D = delete 
DECLARE @insertedCount int, 
     @deletedCount int 
SET @insertedCount = (SELECT COUNT(*) FROM inserted) 
SET @deletedCount = (SELECT COUNT(*) FROM deleted) 

-- handle no action 
IF @insertedCount = 0 AND @deletedCount = 0 RETURN 

-- handle update 
IF @insertedCount <> 0 AND @deletedCount <> 0 
BEGIN 
    INSERT Audit (Type, TableName, UpdateDate, UpdatedBy, PK1) 
    SELECT 
     'U', 
     @table, 
     @timestamp, 
     @updatedBy, 
     CONVERT(VARCHAR(255), i.Id) 
    FROM 
     (SELECT Id, BINARY_CHECKSUM(*) Version FROM inserted) i 
     INNER JOIN 
     (SELECT Id, BINARY_CHECKSUM(*) Version FROM deleted) d 
     ON i.Id = d.Id 
    WHERE 
     i.Version <> d.Version 

    RETURN  
END 

-- handle deletes and inserts 
INSERT Audit (Type, TableName, UpdateDate, UpdatedBy, PK1) 
SELECT   
    CASE 
     WHEN i.Id IS NOT NULL AND d.Id IS NULL THEN 'I' 
     WHEN i.Id IS NULL AND d.Id IS NOT NULL THEN 'D' 
    END, 
    @table, 
    @timestamp, 
    @updatedBy, 
    CONVERT(VARCHAR(255), COALESCE(i.Id, d.Id)) 
FROM inserted i 
     FULL OUTER JOIN 
    deleted d 
     ON i.Id = d.Id 
WHERE i.Id IS NULL OR 
     d.Id IS NULL 

END 

該解決方案不是通用的,因爲需要爲每個表拼寫主鍵。

+0

如果你運行一個MERGE語句並且一些記錄被更新了,而另一些被插入了,我會認爲你將在同一個插入表中(我沒有測試過這個,但它只是合乎邏輯)。在刪除的表中。所以我認爲你不能保證操作中的所有記錄只是一種操作類型。你是否關心它是什麼類型的操作?也許更多關於你想要完成的事情會幫助我們幫助你。 – HLGEM

+0

我想創建一個可靠的審計線索。在其他SOF成員的幫助下,我創建了一個查詢來查找更新的ID。如果不能保證上述邏輯有效,我需要創建查詢來查找新插入和刪除的記錄。 –

+1

@CandyChiu - 對於這種情況,我通常只是在PK上進行完整的外部連接,並檢查'​​INSERTED.PK是否爲空',那麼它就是一個刪除。如果'DELETED.PK是NULL',那麼它是一個插入,否則它是一個更新。如果PK本身是可更新的,那麼這將顯示爲插入/刪除,但在SQL Server中沒有辦法使用觸發器。 –

回答

1

在發表我的評論後,我對MERGE中的插入和刪除表格感到好奇。下面是一個例子:

create table #test (test1 int identity not null, test2 varchar(34) null) 


Insert into #test (test2) 
values('test'), ('test2') 

select * from #test 


declare @output table(test1I int, test1D int, test2I varchar (34), test2D varchar (34)) 


MERGE #test AS target 
USING (SELECT test1, test2 from #test) AS source (test1, test2) 
ON (target.test1 = source.test1) 
WHEN MATCHED AND target.test1 = 1 
    THEN UPDATE SET target.test2 = 'test3' 
WHEN MATCHED 
    THEN DELETE 

OUTPUT inserted.test1, deleted.test1, inserted.test2, deleted.test2 into @output ; 

select 'Inserted/deleted contents', * from @output 
select * from #test 

Drop table #test 

如果您正在進行插入或更新,會發生類似的情況。

如果您需要知道每條記錄,您可能想要使用連接和個案統計來計算每條記錄的狀態,而不是使用標量變量。記住觸發器對插入/更新/刪除整組記錄的操作不是一次一個記錄。所以標量變量的使用常常是你沒有正確做事的線索。如果您給我們更多關於觸發器要做什麼的示例,那麼幫助您解決問題會更容易,因爲目前的方法好像不會覆蓋所有記錄。

基於以上的評論,或許這會給你什麼觸發器中嘗試一些想法:

Insert dbo.Audittable (Id, NewField1Value, OldField1Value, ActionTaken, ActionDate, ActionUser) 
select coalesce(inserted.id, deleted.id) as Id, inserted.field1 as newField1value, deleted.field1 as oldField1value , 
case when inserted.id is not null and deleted.id is not null then 'Update' 
when inserted.id is not null and deleted.id is null then 'Insert' 
when inserted.id is null and deleted.id is not null then 'Delete' 
End as ActionTaken 
, getdate() as ActionDate 
, system_user as ActionUser 
from inserted 
full outer join deleted on inserted.id = deleted.id 

在測試您的觸發,則需要在下面的測試情況至少包括:

  • 單記錄插入
  • 單條記錄刪除
  • 單記錄更新
  • 多張唱片插入
  • 多張記錄刪除
  • 多張記錄更新
  • 多張記錄合併兩者兼具插入和刪除
  • 多張唱片合併,做兩個更新和刪除
  • 多張記錄合併兩者兼具刀片和更新

根據觸發器要做的所有事情,您可能需要更多。

+0

感謝您指出MERGE。 –

+0

我跑了一個類似的例子,我沒有看到插入和更新合併到插入&刪除表的一個實例。 –

+0

您的查詢適用於插入和刪除的合併到更新中。但它不適用於更新,因爲同一行的兩個副本可能會顯示在表中 - 一個在刪除,一個在插入。 –

1

所有4種情況都是可能的。值得注意的是,在更新的情況下爲「both」,在DML語句未修改任何行的情況下爲「none」。

這最後一個條件可以被檢測到更便宜的是與EXISTS查詢:

if @@ROWCOUNT = 0 
    return 

可以爲了提高工作效率,一旦調整你EXIST - 檢查一下,讓每一隻表的查詢。

+0

如何從存儲過程訪問'inserted' /'deleted'? –

+0

插入的表是否可以包含新記錄以及更新的記錄?如果我完全依賴上述邏輯,我會錯過插入的列。 –

+0

@馬丁史密斯,多麼真實!我刪除了該段。 – usr

相關問題