我的任务是开发一个跟踪数据库更改的解决方案。
对于我需要捕获的更新:
删除:
对于插入:
我想到了几种方法:
我正在使用asp.net,C#,sql server 2005,iis6,windows 2003.我没有预算,所以很遗憾我买不到任何东西来帮助我。
感谢您的回答!
答案 0 :(得分:4)
我讨厌支持这个问题而且我知道你没有预算,但最简单的解决方案是升级到SQL Server 2008.它具有此功能built in。我认为至少应该提到遇到这个问题的其他人,即使你不能自己使用它。
(在SQL 2008的可部署版本中,此功能仅在Enterprise中可用。)
答案 1 :(得分:4)
由于一系列原因,触发器不具备您需要的所有信息 - 但没有用户ID是关键。
我会说你在正确的轨道上有一个普通的sp插入任何改变的地方。如果你正在为你的界面标准化sp,那么你就会领先于游戏 - 很难潜入未被跟踪的变化。
将此视为会计应用程序中审计跟踪的等效项 - 这是日记帐 - 记录每笔交易的单个表。他们不会为存款,取款,调整等实施单独的期刊,这也是同样的原则。
答案 2 :(得分:3)
我建议你在每张表中使用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 列中
答案 3 :(得分:1)
我看到过这种处理的一种方式(虽然我不推荐它,老实说)是通过存储过程处理它,传入userid / username /以及作为参数。存储过程将调用日志记录过程,该过程在中央日志表中写入相关详细信息。
虽然这里有点令人讨厌......
对于INSERT / UPDATE,一旦INSERT / UPDATE成功完成,相关行就作为XML数据存储在表中。对于DELETE,行在DELETE运行之前存储(但实际上,他们可以从DELETE语句的输出中获取它 - 至少在SQL Server 2005中)。
如果我没记错的话,该表只有几列:UserID,日志记录的日期时间,事务类型(I / U / D),包含相关行的XML数据,表名和主键值(主要用于快速搜索他们想要的记录。
虽然有很多方法可以给猫皮肤......
我的建议是保持简单。如果/需要,请稍后将其展开。
如果您有能力这样做,请锁定用户只能通过存储过程对表执行可操作的语句,然后从那里处理日志记录(无论如何)。
答案 4 :(得分:0)
我们构建了自己的,只需要将用户和pc传递到每个添加/更新存储过程。然后只需要获取原始记录并填充变量并将它们与传入的变量进行比较并将数据记录到表中。 对于删除,我们只有原始表的副本+时间戳字段,因此记录永远不会被删除,并且可以随时恢复(显然删除例程检查FK关系等)。
添加/更新日志表看起来像 约会时间, TABLE_NAME, 列名, RECORD_ID, OLD_VALUE, NEW_VALUE, 用户身份, 计算机
我们从不插入空值,因此我们将它们转换为空字符串,新条目在old_value列中标记为“{new entry}”。 record_id由多个关键列组成,用于唯一标识该单个记录(field1 +'。'+ field2 + ...)
答案 5 :(得分:0)
首先,在所有表中,您应该至少将这些列添加到数据列DateCreated,UserCreated,DateModified,UserModified。可能您可能希望添加“状态”或“LastAction”列,以便您实际上不会删除只是将其设置为已删除/插入/更新状态的行。接下来,您可以创建一个“历史表”,它是第一个表的精确副本。然后在任何更新或删除时触发器将Deleted表条目复制到History表中,同时更改DateModified,UserModified和Status字段。
答案 6 :(得分:0)
我在SQL Server中进行了一次设置,我们将使用视图来访问我们的数据,这些数据将使用INSTEAD OF触发器来处理插入,更新和删除。
例如:视图上的 INSTEAD OF DELETE 触发器会将基础表中的记录标记为已删除,并且视图已过滤为不显示已删除的记录。
在所有触发器中,我们更新了修改日期和用户名。问题是记录数据库用户名,这与最终应用程序用户名不同。
视图需要以模式绑定才能使其生效。
答案 7 :(得分:0)
关于记录更改数据库的用户:您可以根据需要为数据库创建尽可能多的SQL用户,以及是否使用会话和对程序/脚本的限制/注册访问权限 您可以使用该信息启动不同的数据库连接设置(即用户名), 在使用DB进行任何操作之前
至少对于PHP智能脚本来说应该是可行的,但我对asp.net可能是错误的。