gpt4 book ai didi

sql-server - SQL Server 历史表 - 通过 SP 或触发器填充?

转载 作者:行者123 更新时间:2023-12-01 22:46:58 25 4
gpt4 key购买 nike

在我的应用程序的 SQL Server 后端中,我想为一堆关键表创建历史表,它将跟踪行更改的历史记录。

我的整个应用程序都使用存储过程,没有嵌入 SQL。修改这些表的唯一数据库连接将通过应用程序和 SP 接口(interface)进行。传统上,我合作过的商店都是使用触发器执行此任务。

如果我可以在存储过程和触发器之间进行选择,哪个更好?哪个更快?

最佳答案

触发器。

我们编写了一个 GUI(内部称为“Red Matrix Reloaded”),以便轻松创建/管理审核日志记录触发器。

这是所使用的一些 DDL:

<小时/>

审核日志表

CREATE TABLE [AuditLog] (
[AuditLogID] [int] IDENTITY (1, 1) NOT NULL ,
[ChangeDate] [datetime] NOT NULL CONSTRAINT [DF_AuditLog_ChangeDate] DEFAULT (getdate()),
[RowGUID] [uniqueidentifier] NOT NULL ,
[ChangeType] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[TableName] [varchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[FieldName] [varchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[OldValue] [varchar] (8000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[NewValue] [varchar] (8000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[Username] [varchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[Hostname] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[AppName] [varchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[UserGUID] [uniqueidentifier] NULL ,
[TagGUID] [uniqueidentifier] NULL ,
[Tag] [varchar] (8000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
)
<小时/>

触发记录插入

CREATE TRIGGER LogInsert_Nodes ON dbo.Nodes
FOR INSERT
AS

/* Load the saved context info UserGUID */
DECLARE @SavedUserGUID uniqueidentifier

SELECT @SavedUserGUID = CAST(context_info as uniqueidentifier)
FROM master.dbo.sysprocesses
WHERE spid = @@SPID

DECLARE @NullGUID uniqueidentifier
SELECT @NullGUID = '{00000000-0000-0000-0000-000000000000}'

IF @SavedUserGUID = @NullGUID
BEGIN
SET @SavedUserGUID = NULL
END

/*We dont' log individual field changes Old/New because the row is new.
So we only have one record - INSERTED*/

INSERT INTO AuditLog(
ChangeDate, RowGUID, ChangeType,
Username, HostName, AppName,
UserGUID,
TableName, FieldName,
TagGUID, Tag,
OldValue, NewValue)

SELECT
getdate(), --ChangeDate
i.NodeGUID, --RowGUID
'INSERTED', --ChangeType
USER_NAME(), HOST_NAME(), APP_NAME(),
@SavedUserGUID, --UserGUID
'Nodes', --TableName
'', --FieldName
i.ParentNodeGUID, --TagGUID
i.Caption, --Tag
null, --OldValue
null --NewValue
FROM Inserted i
<小时/>

触发记录更新

CREATE TRIGGER LogUpdate_Nodes ON dbo.Nodes
FOR UPDATE AS

/* Load the saved context info UserGUID */
DECLARE @SavedUserGUID uniqueidentifier

SELECT @SavedUserGUID = CAST(context_info as uniqueidentifier)
FROM master.dbo.sysprocesses
WHERE spid = @@SPID

DECLARE @NullGUID uniqueidentifier
SELECT @NullGUID = '{00000000-0000-0000-0000-000000000000}'

IF @SavedUserGUID = @NullGUID
BEGIN
SET @SavedUserGUID = NULL
END

/* ParentNodeGUID uniqueidentifier */
IF UPDATE (ParentNodeGUID)
BEGIN
INSERT INTO AuditLog(
ChangeDate, RowGUID, ChangeType,
Username, HostName, AppName,
UserGUID,
TableName, FieldName,
TagGUID, Tag,
OldValue, NewValue)
SELECT
getdate(), --ChangeDate
i.NodeGUID, --RowGUID
'UPDATED', --ChangeType
USER_NAME(), HOST_NAME(), APP_NAME(),
@SavedUserGUID, --UserGUID
'Nodes', --TableName
'ParentNodeGUID', --FieldName
i.ParentNodeGUID, --TagGUID
i.Caption, --Tag
d.ParentNodeGUID, --OldValue
i.ParentNodeGUID --NewValue
FROM Inserted i
INNER JOIN Deleted d
ON i.NodeGUID = d.NodeGUID
WHERE (d.ParentNodeGUID IS NULL AND i.ParentNodeGUID IS NOT NULL)
OR (d.ParentNodeGUID IS NOT NULL AND i.ParentNodeGUID IS NULL)
OR (d.ParentNodeGUID <> i.ParentNodeGUID)
END

/* Caption varchar(255) */
IF UPDATE (Caption)
BEGIN
INSERT INTO AuditLog(
ChangeDate, RowGUID, ChangeType,
Username, HostName, AppName,
UserGUID,
TableName, FieldName,
TagGUID, Tag,
OldValue, NewValue)
SELECT
getdate(), --ChangeDate
i.NodeGUID, --RowGUID
'UPDATED', --ChangeType
USER_NAME(), HOST_NAME(), APP_NAME(),
@SavedUserGUID, --UserGUID
'Nodes', --TableName
'Caption', --FieldName
i.ParentNodeGUID, --TagGUID
i.Caption, --Tag
d.Caption, --OldValue
i.Caption --NewValue
FROM Inserted i
INNER JOIN Deleted d
ON i.NodeGUID = d.NodeGUID
WHERE (d.Caption IS NULL AND i.Caption IS NOT NULL)
OR (d.Caption IS NOT NULL AND i.Caption IS NULL)
OR (d.Caption <> i.Caption)
END

...

/* ImageGUID uniqueidentifier */
IF UPDATE (ImageGUID)
BEGIN
INSERT INTO AuditLog(
ChangeDate, RowGUID, ChangeType,
Username, HostName, AppName,
UserGUID,
TableName, FieldName,
TagGUID, Tag,
OldValue, NewValue)
SELECT
getdate(), --ChangeDate
i.NodeGUID, --RowGUID
'UPDATED', --ChangeType
USER_NAME(), HOST_NAME(), APP_NAME(),
@SavedUserGUID, --UserGUID
'Nodes', --TableName
'ImageGUID', --FieldName
i.ParentNodeGUID, --TagGUID
i.Caption, --Tag
(SELECT Caption FROM Nodes WHERE NodeGUID = d.ImageGUID), --OldValue
(SELECT Caption FROM Nodes WHERE NodeGUID = i.ImageGUID) --New Value
FROM Inserted i
INNER JOIN Deleted d
ON i.NodeGUID = d.NodeGUID
WHERE (d.ImageGUID IS NULL AND i.ImageGUID IS NOT NULL)
OR (d.ImageGUID IS NOT NULL AND i.ImageGUID IS NULL)
OR (d.ImageGUID <> i.ImageGUID)
END
<小时/>

触发记录删除

CREATE TRIGGER LogDelete_Nodes ON dbo.Nodes
FOR DELETE
AS

/* Load the saved context info UserGUID */
DECLARE @SavedUserGUID uniqueidentifier

SELECT @SavedUserGUID = CAST(context_info as uniqueidentifier)
FROM master.dbo.sysprocesses
WHERE spid = @@SPID

DECLARE @NullGUID uniqueidentifier
SELECT @NullGUID = '{00000000-0000-0000-0000-000000000000}'

IF @SavedUserGUID = @NullGUID
BEGIN
SET @SavedUserGUID = NULL
END

/*We dont' log individual field changes Old/New because the row is new.
So we only have one record - DELETED*/

INSERT INTO AuditLog(
ChangeDate, RowGUID, ChangeType,
Username, HostName, AppName,
UserGUID,
TableName, FieldName,
TagGUID, Tag,
OldValue,NewValue)

SELECT
getdate(), --ChangeDate
d.NodeGUID, --RowGUID
'DELETED', --ChangeType
USER_NAME(), HOST_NAME(), APP_NAME(),
@SavedUserGUID, --UserGUID
'Nodes', --TableName
'', --FieldName
d.ParentNodeGUID, --TagGUID
d.Caption, --Tag
null, --OldValue
null --NewValue
FROM Deleted d
<小时/>

为了了解软件中的哪个用户进行了更新,每个连接都通过调用存储过程“将自身登录到 SQL Server”:

CREATE PROCEDURE dbo.SaveContextUserGUID @UserGUID uniqueidentifier AS

/* Saves the given UserGUID as the session's "Context Information" */
IF @UserGUID IS NULL
BEGIN
PRINT 'Emptying CONTEXT_INFO because of null @UserGUID'
DECLARE @BinVar varbinary(128)
SET @BinVar = CAST( REPLICATE( 0x00, 128 ) AS varbinary(128) )
SET CONTEXT_INFO @BinVar
RETURN 0
END

DECLARE @UserGUIDBinary binary(16) --a guid is 16 bytes
SELECT @UserGUIDBinary = CAST(@UserGUID as binary(16))
SET CONTEXT_INFO @UserGUIDBinary


/* To load the guid back
DECLARE @SavedUserGUID uniqueidentifier

SELECT @SavedUserGUID = CAST(context_info as uniqueidentifier)
FROM master.dbo.sysprocesses
WHERE spid = @@SPID

select @SavedUserGUID AS UserGUID
*/
<小时/>

注释

  • Stackoverflow 代码格式删除了大部分空白行 - 因此格式很糟糕
  • 我们使用用户表,而不是集成安全性
  • 提供此代码是为了方便 - 不允许对我们的设计选择提出批评。纯粹主义者可能坚持认为所有日志代码都应该在业务层完成 - 他们可以来这里为我们编写/维护它。
  • 无法使用 SQL Server 中的触发器来记录 Blob(Blob 不存在“之前”版本 - 只有当前版本)。 Text 和 nText 是 blob - 这使得注释要么不可记录,要么使它们成为 varchar(2000) 的。
  • 标签列用作任意文本来标识该行(例如,如果删除了客户,则该标签将在审核日志表中显示“通用汽车北美”。
  • TagGUID 用于指向行的“父行”。例如,记录 InvoiceLineItems 指向 InvoiceHeader。这样,任何搜索与特定发票相关的审核日志条目的人都可以在审核跟踪中通过行项目的 TagGUID 找到已删除的“行项目”。
  • 有时,“OldValue”和“NewValue”值会写为子选择 - 以获得有意义的字符串。即”

    旧值:{233d-ad34234..}新值:{883-sdf34...}

在审计跟踪中的用处不如:

OldValue: Daimler Chrysler
NewValue: Cerberus Capital Management

最后说明:请随意不做我们所做的事情。这对我们来说很好,但其他人可以自由地不使用它。

关于sql-server - SQL Server 历史表 - 通过 SP 或触发器填充?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/349524/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com