2014-01-30 129 views
2

我有一個名爲Prices的SQL Server表,它包含數萬行數據。該表格被傳統應用程序嚴重使用,不幸的是無法修改(不能添加,刪除或修改列)。將插入或更新另一個表的SQL Server TRIGGER

我的要求是跟蹤表的修改時間(INSERT, UPDATE, or DELETE)。但是,Prices表沒有LastUpdated列,我無法添加此列。此外,我的觸發器必須與SQL Server 2005

我不過可以創建一個額外的表,PricesHistory將存儲PriceIDUpdateTypeLastUpdated列兼容。

我想一個SQL TRIGGER附加到Prices表要麼INSERTUPDATEPricesHistory表中的行,這將跟蹤,當價格最後更新,哪些操作觸發它的。

這是我到目前爲止,它將檢測哪個操作導致觸發器觸發。然而,我很難從inserteddeletedSELECT表中做出正確的INSERT/UPDATEPricesHistory表。

基本上,所有的操作都應該檢查PriceIDPriceHistory表已經存在,並且UPDATEUpdateTypeLastUpdated列。如果PriceID尚不存在,它應該INSERT它連同UpdateTypeLastUpdated值。

編輯:它已經被同事的inserteddeleted項目行錶帶到了我的注意。這意味着我可以做一個簡單的IF EXISTS ... UPDATE ELSE INSERT INTO條款。這是真的?我的印象是它將成爲行的表格,而不是單獨的行。

CREATE TRIGGER PricesUpdateTrigger 
ON Prices 
AFTER INSERT, UPDATE, DELETE 
AS 
DECLARE @UpdateType nvarchar(1) 
DECLARE @UpdatedDT datetime 

SELECT @UpdatedDT = CURRENT_TIMESTAMP 

IF EXISTS (SELECT * FROM inserted) 
    IF EXISTS (SELECT * FROM deleted) 
     SELECT @UpdateType = 'U' -- Update Trigger 
    ELSE 
     SELECT @UpdateType = 'I' -- Insert Trigger 
ELSE 
    IF EXISTS (SELECT * FROM deleted) 
     SELECT @UpdateType = 'D' -- Delete Trigger 
    ELSE 
     SELECT @UpdateType = NULL; -- Unknown Operation 

IF @UpdateType = 'I' 
BEGIN 
    -- Log an insertion record 
END 

IF @UpdateType = 'U' 
BEGIN 
    -- Log an update record 
END 

IF @UpdateType = 'D' 
BEGIN 
    -- Log a deletion record 
END 

GO 
+0

插入/更新不是表!不適用於MS SQL Server。也許你的同事正在把事情與甲骨文混合起來。對於MS SQL Server,它可以是隻有一行的表,但它是一張表。 – jean

+0

對不起,我的意思是它是一排單排。如果你做了一個批量的'UPDATE'或'DELETE',觸發器會觸發每一行。 – Erik

+1

不,觸發器會在每次操作時觸發,而不是每行觸發。如果您的操作只是更新,則會在您的應用程序特定行爲時插入一行。我只是想爲觀衆澄清一下。 – jean

回答

4

爲什麼不是通用的審計表?查看我的演示文稿「如何預防和審覈更改?」

http://craftydba.com/?page_id=880

這裏是一個表以保存被更改的數據。

-- 
-- 7 - Auditing data changes (table for DML trigger) 
-- 


-- Delete existing table 
IF OBJECT_ID('[AUDIT].[LOG_TABLE_CHANGES]') IS NOT NULL 
    DROP TABLE [AUDIT].[LOG_TABLE_CHANGES] 
GO 


-- Add the table 
CREATE TABLE [AUDIT].[LOG_TABLE_CHANGES] 
(
    [CHG_ID] [numeric](18, 0) IDENTITY(1,1) NOT NULL, 
    [CHG_DATE] [datetime] NOT NULL, 
    [CHG_TYPE] [varchar](20) NOT NULL, 
    [CHG_BY] [nvarchar](256) NOT NULL, 
    [APP_NAME] [nvarchar](128) NOT NULL, 
    [HOST_NAME] [nvarchar](128) NOT NULL, 
    [SCHEMA_NAME] [sysname] NOT NULL, 
    [OBJECT_NAME] [sysname] NOT NULL, 
    [XML_RECSET] [xml] NULL, 
CONSTRAINT [PK_LTC_CHG_ID] PRIMARY KEY CLUSTERED ([CHG_ID] ASC) 
) ON [PRIMARY] 
GO 

-- Add defaults for key information 
ALTER TABLE [AUDIT].[LOG_TABLE_CHANGES] ADD CONSTRAINT [DF_LTC_CHG_DATE] DEFAULT (getdate()) FOR [CHG_DATE]; 
ALTER TABLE [AUDIT].[LOG_TABLE_CHANGES] ADD CONSTRAINT [DF_LTC_CHG_TYPE] DEFAULT ('') FOR [CHG_TYPE]; 
ALTER TABLE [AUDIT].[LOG_TABLE_CHANGES] ADD CONSTRAINT [DF_LTC_CHG_BY] DEFAULT (coalesce(suser_sname(),'?')) FOR [CHG_BY]; 
ALTER TABLE [AUDIT].[LOG_TABLE_CHANGES] ADD CONSTRAINT [DF_LTC_APP_NAME] DEFAULT (coalesce(app_name(),'?')) FOR [APP_NAME]; 
ALTER TABLE [AUDIT].[LOG_TABLE_CHANGES] ADD CONSTRAINT [DF_LTC_HOST_NAME] DEFAULT (coalesce(host_name(),'?')) FOR [HOST_NAME]; 
GO 

這是一個觸發器來捕獲INS,UPD,DEL語句。

-- 
-- 8 - Make DML trigger to capture changes 
-- 


-- Delete existing trigger 
IF OBJECT_ID('[ACTIVE].[TRG_FLUID_DATA]') IS NOT NULL 
    DROP TRIGGER [ACTIVE].[TRG_FLUID_DATA] 
GO 

-- Add trigger to log all changes 
CREATE TRIGGER [ACTIVE].[TRG_FLUID_DATA] ON [ACTIVE].[CARS_BY_COUNTRY] 
    FOR INSERT, UPDATE, DELETE AS 
BEGIN 

    -- Detect inserts 
    IF EXISTS (select * from inserted) AND NOT EXISTS (select * from deleted) 
    BEGIN 
    INSERT [AUDIT].[LOG_TABLE_CHANGES] ([CHG_TYPE], [SCHEMA_NAME], [OBJECT_NAME], [XML_RECSET]) 
    SELECT 'INSERT', '[ACTIVE]', '[CARS_BY_COUNTRY]', (SELECT * FROM inserted as Record for xml auto, elements , root('RecordSet'), type) 
    RETURN; 
    END 

    -- Detect deletes 
    IF EXISTS (select * from deleted) AND NOT EXISTS (select * from inserted) 
    BEGIN 
    INSERT [AUDIT].[LOG_TABLE_CHANGES] ([CHG_TYPE], [SCHEMA_NAME], [OBJECT_NAME], [XML_RECSET]) 
    SELECT 'DELETE', '[ACTIVE]', '[CARS_BY_COUNTRY]', (SELECT * FROM deleted as Record for xml auto, elements , root('RecordSet'), type) 
    RETURN; 
    END 

    -- Update inserts 
    IF EXISTS (select * from inserted) AND EXISTS (select * from deleted) 
    BEGIN 
    INSERT [AUDIT].[LOG_TABLE_CHANGES] ([CHG_TYPE], [SCHEMA_NAME], [OBJECT_NAME], [XML_RECSET]) 
    SELECT 'UPDATE', '[ACTIVE]', '[CARS_BY_COUNTRY]', (SELECT * FROM deleted as Record for xml auto, elements , root('RecordSet'), type) 
    RETURN; 
    END 

END; 
GO 

如果你有一個批量修改表,然後的一個週期或者清除數據,或只記錄修改日期在另一個表像你說。但是,關鍵信息將會丟失。

我的解決方案的好處是,它告訴你什麼時候和誰做了這些改變。實際數據以XML格式保存,如果需要可以恢復。

+0

這真的很整齊,雖然可能乍一看在我頭上。我會看看它,看看我能不能做類似的事情。我很確定這個想法! – Erik

+0

下載我的演示文稿包。創建AUTOS數據庫並玩耍。這並不難。 –

+0

我喜歡這個主意!我想我會修改和使用我們的刪除審計模板,但我更喜歡更新的舊/新值,以及僅用於插入的PK。有一點需要注意的是,如果你有一個大規模的插入,更新或刪除,那麼你可能會達到XML數據類型的2GB最大限制,這可能會導致你的交易中斷(儘管它必須是很多正在更新的數據)。 – BateTech

0

我從那裏得到的代碼可以得到如何編寫單觸發器來處理數據庫插入更新以及如何通過觸發器執行審計跟蹤的視圖。希望這個幫助。

CREATE TRIGGER TRG_HourSheet ON EditedHourSheet 
FOR INSERT, UPDATE 
AS      
    DECLARE @v_xml XML, 
    @PKValue INT,      
    @type CHAR(1), 
    @v_slno INT    

BEGIN      
SET NOCOUNT ON      


IF EXISTS(SELECT * FROM INSERTED) 
BEGIN 
    IF EXISTS(SELECT * FROM DELETED) 
    BEGIN 
    SET @type ='U'; 
    END 
    ELSE 
    BEGIN 
    SET @type ='I'; 
    END 
END 

IF @type = 'U' 
BEGIN 
    DECLARE DB_CURSOR CURSOR FOR      
    SELECT ID FROM DELETED ORDER BY ModDate DESC      

    OPEN DB_CURSOR      
    FETCH NEXT FROM DB_CURSOR INTO @PKValue      
    WHILE @@FETCH_STATUS = 0      
    BEGIN      
    SET @v_xml =(SELECT * FROM DELETED Where [email protected]   
     FOR xml AUTO, root('Record'),elements XSINIL)      

    SELECT @v_slno = IsNull(Max(RowID),0)+1 FROM EditedHourSheetLog 
    Where [email protected]   

    INSERT INTO EditedHourSheetLog(HourSheetID,XMLData,Action,RowID)      
    values (@PKValue,@v_xml,@type,@v_slno)      
    FETCH NEXT FROM DB_CURSOR INTO @PKValue      

    END      

    CLOSE DB_CURSOR      
    DEALLOCATE DB_CURSOR      
    --END 
END 
ELSE IF @type = 'I' 
BEGIN 
    DECLARE DB_CURSOR CURSOR FOR      
    SELECT ID FROM INSERTED ORDER BY ModDate DESC      

    OPEN DB_CURSOR      
    FETCH NEXT FROM DB_CURSOR INTO @PKValue      
    WHILE @@FETCH_STATUS = 0      
    BEGIN      
    SET @v_xml =(SELECT * FROM INSERTED Where [email protected]   
     FOR xml AUTO, root('Record'),elements XSINIL)      

    SELECT @v_slno = IsNull(Max(RowID),0)+1 FROM EditedHourSheetLog 
    Where [email protected]   

    INSERT INTO EditedHourSheetLog(HourSheetID,XMLData,Action,RowID)      
    values (@PKValue,@v_xml,@type,@v_slno)      
    FETCH NEXT FROM DB_CURSOR INTO @PKValue      

    END      

    CLOSE DB_CURSOR      
    DEALLOCATE DB_CURSOR      
    --END 
END 
END 
+0

在任何情況下都不要在觸發器中使用光標。 – HLGEM

+0

你會介意解釋爲什麼? – Thomas

+0

如果使用update語句手動更新大量行,則觸發器可能無法捕獲所有更新,所以這就是爲什麼我使用遊標。我很清楚。 – Thomas

相關問題