簡體   English   中英

跟蹤SQL Server 2005數據庫中的更改

[英]Tracking changes in a SQL server 2005 database

我的任務是開發一個跟蹤數據庫更改的解決方案。

對於我需要捕獲的更新:

  • 更新日期
  • 舊的價值
  • 新的價值
  • 場受影響
  • 做變化的人
  • 記錄ID
  • 表記錄在

刪除:

  • 刪除日期
  • 做刪除的人
  • 已刪除記錄的標題/描述/ ID。 我跟蹤的表更改都有標題或說明字段。 我想在刪除記錄之前捕獲它。
  • 表記錄在

對於插入:

  • 插入日期
  • 做變化的人
  • 記錄ID
  • 表記錄在

我想過幾種方法可以做到這一點:

  • 我正在使用存儲過程進行任何更新/刪除/插入。 我會創建一個通用的“跟蹤”表。 它將有足夠的字段來捕獲所有數據。 然后,我會在每個存儲過程中添加另一行,以“將記錄插入跟蹤表”的效果。
    • 缺點:所有更新/刪除/插入都在同一個表中混亂
    • 很多空字段
    • 如何跟蹤批量更新/刪除/插入? <----這可能不是問題。 我在應用程序中沒有做任何類似的事情。
    • 如何捕獲進行更新的用戶。 數據庫只看到一個帳戶。
    • 編輯許多現有代碼進行編輯。
  • 最后,我可以創建一個在更新/刪除/插入后調用的觸發器。 許多與第一個解決方案相同的缺點除了:我必須編輯盡可能多的代碼。 我不確定如何跟蹤更新。 它看起來不像使用觸發器來查看最近更新的記錄。

我正在使用asp.net,C#,sql server 2005,iis6,windows 2003.我沒有預算,所以很遺憾,我無法買任何東西來幫助我。

謝謝你的回答!

我討厭支持這個問題而且我知道你沒有預算,但最簡單的解決方案是升級到SQL Server 2008.它內置了這個功能。 我認為至少應該提到遇到這個問題的其他人,即使你不能自己使用它。

(在SQL 2008的可部署版本中,此功能僅在Enterprise中可用。)

由於一系列原因,觸發器不會擁有您需要的所有信息 - 但沒有用戶ID是關鍵。

我會說你在正確的軌道上有一個普通的sp來插入任何改變的地方。 如果你正在為你的界面標准化sp,那么你就領先於游戲 - 很難潛入未被跟蹤的變化。

將此視為會計應用程序中審計跟蹤的等效項 - 這是日記帳 - 記錄每個事務的單個表。 他們不會為存款,取款,調整等實施單獨的期刊,這也是同樣的原則。

我建議你在每張表中使用2列。 名稱為rowhistoryIsDeleted ,數據類型為xml和bit。 永遠不要刪除行,總是使用標志IsDeleted現在使用更新觸發器。 我會舉例說明我有一個名為Page的表

    CREATE TABLE te_Page([Id] [int] IDENTITY(1,1) NOT NULL, [Name] [varchar](200) NOT NULL, [Description] [varchar](200) NULL,[CreatedBy] [uniqueidentifier] NULL, [CreatedDate] [datetime] NOT NULL, [UpdatedBy] [uniqueidentifier] NULL, [UpdatedDate] [datetime] NULL, [IsDeleted] [bit] NULL, [RowHistory] [xml] NULL, CONSTRAINT [PK_tm_Page] PRIMARY KEY CLUSTERED ([Id] ASC )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY] ) ON [PRIMARY]

現在創建表后,您需要做的就是復制粘貼下面的代碼,並為Page表完成任務。 它將開始在同一行中記錄行的歷史記錄,該行與舊值和新值一起更新。

                ALTER Trigger [dbo].[Trg_Te_Page]    
        On [dbo].[te_Page]                
        After Update                
        As                 
        --If @@rowcount = 0 Or Update(RowHistory)    
        --Return    

        Declare @xml NVARCHAR(MAX)     
        Declare @currentxml NVARCHAR(MAX)   
        Declare @node NVARCHAR(MAX)    
        Declare @ishistoryexists XML    

        Declare @FormLineAttributeValueId int  

        -- new Values  
        Declare @new_Name varchar(200)  
        Declare @new_Description varchar(200)  

        Declare @new_CreatedBy UNIQUEIDENTIFIER    
        Declare @new_CreatedDate DATETIME    
        Declare @new_UpdatedBy UNIQUEIDENTIFIER    
        Declare @new_UpdatedDate DATETIME    
        Declare @new_IsDeleted BIT  

        --old values  
        Declare @old_Name varchar(200)  
        Declare @old_Description varchar(200)  

        Declare @old_CreatedBy UNIQUEIDENTIFIER    
        Declare @old_CreatedDate DATETIME    
        Declare @old_UpdatedBy UNIQUEIDENTIFIER    
        Declare @old_UpdatedDate DATETIME    
        Declare @old_IsDeleted BIT  


        -- declare temp fmId  
        Declare @fmId int  
        -- declare cursor  
        DECLARE curFormId cursor   
        FOR select Id from INSERTED   
        -- open cursor       
        OPEN curFormId  
        -- fetch row  
        FETCH NEXT FROM curFormId INTO @fmId  

        WHILE @@FETCH_STATUS  = 0   
        BEGIN   

        Select   
        @FormLineAttributeValueId = Id,   
        @old_Name = Name,  
        @old_Description = [Description],  

        @old_CreatedBy = CreatedBy,    
        @old_CreatedDate =CreatedDate,  
        @old_UpdatedBy =UpdatedBy,    
        @old_UpdatedDate =UpdatedDate,  
        @old_IsDeleted  = IsDeleted,  
        @currentxml = cast(RowHistory as NVARCHAR(MAX))  
        From DELETED where Id=@fmId  



        Select      
        @new_Name = Name,  
        @new_Description = [Description],  

        @new_CreatedBy = CreatedBy,    
        @new_CreatedDate =CreatedDate,  
        @new_UpdatedBy =UpdatedBy,    
        @new_UpdatedDate =UpdatedDate,  
        @new_IsDeleted  = IsDeleted  
        From INSERTED where Id=@fmId  

        set @old_Name = Replace(@old_Name,'&','&amp;')
        set @old_Name = Replace(@old_Name,'>','&gt;')  
        set @old_Name = Replace(@old_Name,'<','&lt;')     
        set @old_Name = Replace(@old_Name,'"','&quot;')
        set @old_Name = Replace(@old_Name,'''','&apos;')          

        set @new_Name = Replace(@new_Name,'&','&amp;')      
        set @new_Name = Replace(@new_Name,'>','&gt;')  
        set @new_Name = Replace(@new_Name,'<','&lt;')     
        set @new_Name = Replace(@new_Name,'"','&quot;')
        set @new_Name = Replace(@new_Name,'''','&apos;') 

        set @old_Description = Replace(@old_Description,'&','&amp;')
        set @old_Description = Replace(@old_Description,'>','&gt;')  
        set @old_Description = Replace(@old_Description,'<','&lt;')     
        set @old_Description = Replace(@old_Description,'"','&quot;')
        set @old_Description = Replace(@old_Description,'''','&apos;')          

        set @new_Description = Replace(@new_Description,'&','&amp;')      
        set @new_Description = Replace(@new_Description,'>','&gt;')  
        set @new_Description = Replace(@new_Description,'<','&lt;')     
        set @new_Description = Replace(@new_Description,'"','&quot;')
        set @new_Description = Replace(@new_Description,'''','&apos;')   

        set @xml = ''     

        BEGIN      

        -- for Name  
        If ltrim(rtrim(IsNull(@new_Name,''))) != ltrim(rtrim(IsNull(@old_Name,'')))    
        set @xml = @xml + '<ColumnInfo ColumnName="Name" OldValue="'+ @old_Name + '" NewValue="' + @new_Name + '"/>'    

        -- for Description  
        If ltrim(rtrim(IsNull(@new_Description,''))) != ltrim(rtrim(IsNull(@old_Description,'')))    
        set @xml = @xml + '<ColumnInfo ColumnName="Description" OldValue="'+ @old_Description + '" NewValue="' + @new_Description + '"/>'    

        -- CreatedDate     
        If IsNull(@new_CreatedDate,'') != IsNull(@old_CreatedDate,'')  
        set @xml = @xml + '<ColumnInfo ColumnName="CreatedDate" OldValue="'+ cast(isnull(@old_CreatedDate,'') as varchar(100)) + '" NewValue="' + cast(isnull(@new_CreatedDate,'') as varchar(100)) + '"/>'    

        -- CreatedBy     
        If cast(IsNull(@new_CreatedBy,'00000000-0000-0000-0000-000000000000')as varchar (36)) != cast(IsNull(@old_CreatedBy,'00000000-0000-0000-0000-000000000000')as varchar(36))    
        set @xml = @xml + '<ColumnInfo ColumnName="CreatedBy" OldValue="'+ cast(IsNull(@old_CreatedBy,'00000000-0000-0000-0000-000000000000') as varchar(36)) + '" NewValue="' + cast(isnull(@new_CreatedBy,'00000000-0000-0000-0000-000000000000') as varchar(36))+
        '"/>'    

        -- UpdatedDate       
        If IsNull(@new_UpdatedDate,'') != IsNull(@old_UpdatedDate,'')    
        set @xml = @xml + '<ColumnInfo ColumnName="UpdatedDate" OldValue="'+ cast(IsNull(@old_UpdatedDate,'') as varchar(100)) + '" NewValue="' + cast(IsNull(@new_UpdatedDate,'') as varchar(100)) + '"/>'    

        -- UpdatedBy     
        If cast(IsNull(@new_UpdatedBy,'00000000-0000-0000-0000-000000000000') as varchar(36)) != cast(IsNull(@old_UpdatedBy,'00000000-0000-0000-0000-000000000000') as varchar(36))    
        set @xml = @xml + '<ColumnInfo ColumnName="UpdatedBy" OldValue="'+ cast(IsNull(@old_UpdatedBy,'00000000-0000-0000-0000-000000000000') as varchar(36)) + '" NewValue="' + cast(IsNull(@new_UpdatedBy,'00000000-0000-0000-0000-000000000000') as varchar(36))+
        '"/>'    

        -- IsDeleted  
        If cast(IsNull(@new_IsDeleted,'') as varchar(10)) != cast(IsNull(@old_IsDeleted,'') as varchar(10))    
        set @xml = @xml + '<ColumnInfo ColumnName="IsDeleted" OldValue="'+ cast(IsNull(@old_IsDeleted,'') as varchar(10)) + '" NewValue="' + cast(IsNull(@new_IsDeleted,'') as varchar(10)) + '" />'    

        END    

        Set @xml = '<RowInfo TableName="te_Page" UpdatedBy="' + cast(IsNull(@new_UpdatedBy,'00000000-0000-0000-0000-000000000000') as varchar(50)) +  '" UpdatedDate="' + Convert(Varchar(20),GetDate()) + '">' + @xml + '</RowInfo>'    
        Select @ishistoryexists = RowHistory From DELETED     

        --print @ishistoryexists  


        If @ishistoryexists is null    
        Begin     
        Set @xml = '<History>' + @xml + '</History>'      
        Update te_Page    
        Set    
        RowHistory = @xml    
        Where     
        Id = @FormLineAttributeValueId    

        End    

        Else    
        Begin     
        set @xml = REPLACE(@currentxml, '<History>', '<History>' + @xml)  
        Update te_Page  
        Set  
        RowHistory = @xml  
        Where   
        Id = @FormLineAttributeValueId     
        End  


        FETCH NEXT FROM curFormId INTO @fmId  
        END   


        CLOSE curFormId  
        DEALLOCATE curFormId  

現在,無論何時執行任何更新,您的數據都將存儲在rowhistory列中

我看到這種方法的一種方法(雖然我不推薦它,老實說)是通過存儲過程處理它,傳入userid / username /以及作為參數。 存儲過程將調用日志記錄過程,該過程在中央日志表中寫入相關詳細信息。

這里有點令人討厭,但......

對於INSERT / UPDATE,一旦INSERT / UPDATE成功完成,相關行就作為XML數據存儲在表中。 對於DELETE,行在DELETE運行之前存儲(但實際上,他們可以從DELETE語句的輸出中獲取它 - 至少在SQL Server 2005中)。

如果我沒記錯的話,該表只有幾列:UserID,日志記錄的日期時間,事務類型(I / U / D),包含相關行的XML數據,表名和主鍵值(主要用於快速搜索)他們想要什么記錄)。

皮膚貓的很多方法,但......

我的建議是保持簡單。 如果/需要,稍后將其展開。

如果您有能力這樣做,請鎖定用戶只能通過存儲過程對表執行可操作的語句,然后從那里處理日志記錄(無論如何)。

關於記錄更改數據庫的用戶:您可以根據需要為數據庫創建盡可能多的SQL用戶,如果使用會話和對程序/腳本的限制/注冊訪問,則可以使用該信息啟動不同的數據庫連接設置(即用戶名) ,在使用DB進行任何操作之前。

至少對於PHP智能腳本應該是可行的,但我對asp.net可能是錯的。

我們構建了自己的,只需要將用戶和pc傳遞到每個添加/更新存儲過程。 然后只需要獲取原始記錄並填充變量並將它們與傳入的變量進行比較並將數據記錄到表中。 對於刪除,我們只有原始表的副本+時間戳字段,因此記錄永遠不會被刪除,並且可以隨時恢復(顯然刪除例程檢查FK關系等)。

添加/更新日志表看起來像datetime,table_name,column_name,record_id,old_value,new_value,user_id,computer

我們從不插入空值,因此我們將它們轉換為空字符串,新條目在old_value列中標記為“{new entry}”。 record_id由多個關鍵列組成,用於唯一標識該單個記錄(field1 +'。'+ field2 + ...)

首先,在所有表中,您應該至少將這些列添加到數據列DateCreated,UserCreated,DateModified,UserModified。 可能您可能希望添加“狀態”或“LastAction”列,以便您實際上不會刪除只是將其設置為已刪除/插入/更新狀態的行。 接下來,您可以創建一個“歷史表”,它是第一個表的精確副本。 然后,在任何更新或刪除時,觸發器會將已刪除的表條目復制到歷史記錄表中,同時更改DateModified,UserModified和Status字段。

我在SQL Server中有一個設置,我們將使用視圖來訪問我們的數據,這些數據將使用INSTEAD OF觸發器處理插入,更新和刪除。

例如:視圖上的INSTEAD OF DELETE觸發器會將基礎表中的記錄標記為已刪除,並且視圖已過濾以不顯示已刪除的記錄。

在所有觸發器中,我們更新了修改日期和用戶名。 問題是記錄數據庫用戶名,這與最終應用程序用戶名不同。

視圖需要是模式綁定才能使其工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM