我有一个名为Prices
的SQL Server表,其中包含数万行数据。遗留应用程序大量使用此表,遗憾的是无法修改此表(无法添加,删除或修改列)。
我的要求是跟踪表的修改时间(INSERT, UPDATE, or DELETE
)。但是,Prices
表没有LastUpdated
列,我无法添加此列。此外,我的触发器必须与SQL Server 2005兼容。
但我可以创建一个额外的表格PricesHistory
,该表格会存储PriceID
,UpdateType
和LastUpdated
列。
我想将SQL TRIGGER
附加到Prices
表格中,INSERT
或UPDATE
表格中的PricesHistory
或SELECT
一行将跟踪当价格最后一次更新以及哪些操作触发了它。
这是我到目前为止所做的,它将检测哪个操作导致触发器触发。但是,我很难从inserted
或deleted
表格中INSERT/UPDATE
进行操作,并对PricesHistory
表做了正确的PriceID
。
基本上,所有操作都应检查PriceHistory
表中是否已存在UPDATE
,UpdateType
和LastUpdated
列中是否PriceID
。如果INSERT
尚不存在,则应UpdateType
以及LastUpdated
和inserted
值。
编辑:一位同事提醒我注意deleted
和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
子句。这是真的?我的印象是它将是行的表,而不是单独的行。
{{1}}
答案 0 :(得分:6)
为什么不是通用审计表?请参阅我的演讲“如何预防和审核变更?”
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格式保存,如果需要可以恢复。
答案 1 :(得分:1)
我从那里给出的代码你可以得到如何编写单个触发器来处理数据库插入和放大的视图。更新以及如何通过触发器执行审计跟踪。希望这有帮助。
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 ID=@PKValue
FOR xml AUTO, root('Record'),elements XSINIL)
SELECT @v_slno = IsNull(Max(RowID),0)+1 FROM EditedHourSheetLog
Where HourSheetID=@PKValue
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 ID=@PKValue
FOR xml AUTO, root('Record'),elements XSINIL)
SELECT @v_slno = IsNull(Max(RowID),0)+1 FROM EditedHourSheetLog
Where HourSheetID=@PKValue
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