[英]Tracking changes in a SQL server 2005 database
我的任務是開發一個跟蹤數據庫更改的解決方案。
對於我需要捕獲的更新:
刪除:
對於插入:
我想過幾種方法可以做到這一點:
我正在使用asp.net,C#,sql server 2005,iis6,windows 2003.我沒有預算,所以很遺憾,我無法買任何東西來幫助我。
謝謝你的回答!
我討厭支持這個問題而且我知道你沒有預算,但最簡單的解決方案是升級到SQL Server 2008.它內置了這個功能。 我認為至少應該提到遇到這個問題的其他人,即使你不能自己使用它。
(在SQL 2008的可部署版本中,此功能僅在Enterprise中可用。)
由於一系列原因,觸發器不會擁有您需要的所有信息 - 但沒有用戶ID是關鍵。
我會說你在正確的軌道上有一個普通的sp來插入任何改變的地方。 如果你正在為你的界面標准化sp,那么你就領先於游戲 - 很難潛入未被跟蹤的變化。
將此視為會計應用程序中審計跟蹤的等效項 - 這是日記帳 - 記錄每個事務的單個表。 他們不會為存款,取款,調整等實施單獨的期刊,這也是同樣的原則。
我建議你在每張表中使用2列。 名稱為rowhistory和IsDeleted ,數據類型為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,'&','&')
set @old_Name = Replace(@old_Name,'>','>')
set @old_Name = Replace(@old_Name,'<','<')
set @old_Name = Replace(@old_Name,'"','"')
set @old_Name = Replace(@old_Name,'''',''')
set @new_Name = Replace(@new_Name,'&','&')
set @new_Name = Replace(@new_Name,'>','>')
set @new_Name = Replace(@new_Name,'<','<')
set @new_Name = Replace(@new_Name,'"','"')
set @new_Name = Replace(@new_Name,'''',''')
set @old_Description = Replace(@old_Description,'&','&')
set @old_Description = Replace(@old_Description,'>','>')
set @old_Description = Replace(@old_Description,'<','<')
set @old_Description = Replace(@old_Description,'"','"')
set @old_Description = Replace(@old_Description,'''',''')
set @new_Description = Replace(@new_Description,'&','&')
set @new_Description = Replace(@new_Description,'>','>')
set @new_Description = Replace(@new_Description,'<','<')
set @new_Description = Replace(@new_Description,'"','"')
set @new_Description = Replace(@new_Description,'''',''')
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.