如何将历史记录存储在SQL Server的历史记录表中

Mau*_*imo 25 sql sql-server

我有2张桌子,Table-ATable-A-History.

  • Table-A 包含当前数据行.
  • Table-A-History 包含历史数据

我想拥有最新的数据行Table-A,并Table-A-History包含历史行.

我可以想到两种方法来实现这个目标:

  1. 每当有新数据行可用时,将当前行Table-A移至Table-A-HistoryTable-A使用最新数据更新行(通过insert into selectselect into table)

    要么

  2. 每当有新数据行可用时,更新Table-A行并插入新行Table-A-History.

关于性能方法1或2更好?有没有更好的不同方法来实现这一目标?

CRA*_*DBA 48

基本上,您希望跟踪/审核对表的更改,同时保持主表的大小.

有几种方法可以解决这个问题.下面讨论每种方式的缺点和优点.

1 - 使用触发器审计表.

如果您要查看表(插入,更新,删除),请查看我如何重新编写不需要的事务 - SQL Saturday slide deck w/code - http://craftydba.com/?page_id=880.如果您选择,填充审计表的触发器可以保存来自多个表的信息,因为数据保存为XML.因此,您可以根据需要通过解析XML来取消删除操作.它跟踪谁和什么做出了改变.

(可选)您可以在其自己的文件组上拥有审计表.

Description:
    Table Triggers For (Insert, Update, Delete)
    Active table has current records.
    Audit (history) table for non-active records.

Pros:
    Active table has smaller # of records.
    Index in active table is small.
    Change is quickly reported in audit table.
    Tells you what change was made (ins, del, upd)

Cons:
    Have to join two tables to do historical reporting.
    Does not track schema changes.
Run Code Online (Sandbox Code Playgroud)

2 - 有效约会记录

如果您永远不会从审计表中清除数据,为什么不将该行标记为已删除但仍保留该行?许多系统如人们使用有效约会来显示记录是否不再有效.在BI世界中,这被称为类型2维表(缓慢变化的维度).请参阅数据仓库研究所文章.http://www.bidw.org/datawarehousing/scd-type-2/每条记录都有一个开始和结束日期.

所有活动记录的结束日期均为null.

Description:
    Table Triggers For (Insert, Update, Delete)
    Main table has both active and historical records.

Pros:
    Historical reporting is easy.
    Change is quickly shown in main table.

Cons:
    Main table has a large # of records.
    Index of main table is large.
    Both active & history records in same filegroup.
    Does not tell you what change was made (ins, del, upd)
    Does not track schema changes.
Run Code Online (Sandbox Code Playgroud)

3 - 更改数据捕获(企业功能).

Micorsoft SQL Server 2008引入了更改数据捕获功能.虽然事实上它使用LOG阅读器跟踪数据变化(CDC),但它缺少诸如谁以及做出改变的内容之类的东西.MSDN详细信息 - http://technet.microsoft.com/en-us/library/bb522489(v=sql.105).aspx

此解决方案取决于正在运行的CDC作业.sql代理的任何问题都会导致数据出现延迟.

请参阅更改数据捕获表. http://technet.microsoft.com/en-us/library/bb500353(v=sql.105).aspx

Description:
    Enable change data capture

Pros:
    Do not need to add triggers or tables to capture data.
    Tells you what change was made (ins, del, upd) the _$operation field in 
    <user_defined_table_CT>
    Tracks schema changes.    

Cons:
    Only available in enterprise version.
    Since it reads the log after the fact, time delay in data showing up.
    The CDC tables do not track who or what made the change.
    Disabling CDC removes the tables (not nice)!
    Need to decode and use the _$update_mask to figure out what columns changed.
Run Code Online (Sandbox Code Playgroud)

4 - 更改跟踪功能(所有版本).

Micorsoft SQL Server 2008引入了更改跟踪功能.与CDC不同,它带有所有版本; 但是,它附带了一堆TSQL函数,您必须调用它们才能弄清楚发生了什么.

它的设计目的是通过应用程序与SQL服务器同步一个数据源.TechNet上有一个完整的同步框架.

http://msdn.microsoft.com/en-us/library/bb933874.aspx http://msdn.microsoft.com/en-us/library/bb933994.aspx http://technet.microsoft.com/en-我们/库/ bb934145(v = SQL.105)的.aspx

与CDC不同,您可以指定在清除之前数据库中的最后更改时间.此外,插入和删除不记录数据.更新仅记录更改的字段.

由于您正在将SQL Server源同步到另一个目标,因此可以正常工作.除非你写一份定期的工作来计算变化,否则对审计不利.

您仍然需要在某处存储该信息.

Description:
    Enable change tracking

Cons:
    Not a good auditing solution
Run Code Online (Sandbox Code Playgroud)

前三个解决方案将适用于您的审计.我喜欢第一个解决方案,因为我在我的环境中广泛使用它.

诚挚

约翰

演示文稿的代码片段(汽车数据库)

-- 
-- 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



--
--  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



--
--  9 - Test DML trigger by updating, deleting and inserting data
--

-- Execute an update
UPDATE [ACTIVE].[CARS_BY_COUNTRY]
SET COUNTRY_NAME = 'Czech Republic'
WHERE COUNTRY_ID = 8
GO

-- Remove all data
DELETE FROM [ACTIVE].[CARS_BY_COUNTRY];
GO

-- Execute the load
EXECUTE [ACTIVE].[USP_LOAD_CARS_BY_COUNTRY];
GO 

-- Show the audit trail
SELECT * FROM [AUDIT].[LOG_TABLE_CHANGES]
GO

-- Disable the trigger
ALTER TABLE [ACTIVE].[CARS_BY_COUNTRY] DISABLE TRIGGER [TRG_FLUID_DATA];
Run Code Online (Sandbox Code Playgroud)

**审核表的外观**

在此输入图像描述


HAB*_*ABO 21

记录更改是我通常使用基表上的触发器来记录日志表中的更改.日志表具有其他列以记录数据库用户,操作和日期/时间.

create trigger Table-A_LogDelete on dbo.Table-A
  for delete
as
  declare @Now as DateTime = GetDate()
  set nocount on
  insert into Table-A-History
    select SUser_SName(), 'delete-deleted', @Now, *
      from deleted
go
exec sp_settriggerorder @triggername = 'Table-A_LogDelete', @order = 'last', @stmttype = 'delete'
go
create trigger Table-A_LogInsert on dbo.Table-A
  for insert
as
  declare @Now as DateTime = GetDate()
  set nocount on
  insert into Table-A-History
    select SUser_SName(), 'insert-inserted', @Now, *
      from inserted
go
exec sp_settriggerorder @triggername = 'Table-A_LogInsert', @order = 'last', @stmttype = 'insert'
go
create trigger Table-A_LogUpdate on dbo.Table-A
  for update
as
  declare @Now as DateTime = GetDate()
  set nocount on
  insert into Table-A-History
    select SUser_SName(), 'update-deleted', @Now, *
      from deleted
  insert into Table-A-History
    select SUser_SName(), 'update-inserted', @Now, *
      from inserted
go
exec sp_settriggerorder @triggername = 'Table-A_LogUpdate', @order = 'last', @stmttype = 'update'
Run Code Online (Sandbox Code Playgroud)

记录触发器应始终设置为最后触发.否则,后续触发器可能会回滚原始事务,但日志表已经更新.这是一个令人困惑的事态.


小智 16

SQL Server(2016+和Azure)的最新版本具有时态表,它提供了所需的确切功能,作为一流的功能. https://docs.microsoft.com/en-us/sql/relational-databases/tables/temporal-tables

微软的某个人可能会阅读此页面.:)