SQL Server history table - populate through SP or Trigger?

前端 未结 11 1811
遥遥无期
遥遥无期 2020-11-28 20:43

In my SQL Server backend for my app, I want to create history tables for a bunch of my key tables, which will track a history of changes to the rows.

My entire appli

11条回答
  •  借酒劲吻你
    2020-11-28 21:38

    We have a third party tool ApexSQL Audit that we used to generate triggers.

    Here is how triggers look like in the background and how data is stored. Hopefully guys will find this useful enough to reverse engineer the process. Its a bit different from what Ian Boyd showed in his examples because it allows each columns to be audited separately.

    Table 1 – holds transaction details (who, when, application, host name, etc)

    CREATE TABLE [dbo].[AUDIT_LOG_TRANSACTIONS](
        [AUDIT_LOG_TRANSACTION_ID] [int] IDENTITY(1,1) NOT NULL,
        [DATABASE] [nvarchar](128) NOT NULL,
        [TABLE_NAME] [nvarchar](261) NOT NULL,
        [TABLE_SCHEMA] [nvarchar](261) NOT NULL,
        [AUDIT_ACTION_ID] [tinyint] NOT NULL,
        [HOST_NAME] [varchar](128) NOT NULL,
        [APP_NAME] [varchar](128) NOT NULL,
        [MODIFIED_BY] [varchar](128) NOT NULL,
        [MODIFIED_DATE] [datetime] NOT NULL,
        [AFFECTED_ROWS] [int] NOT NULL,
        [SYSOBJ_ID]  AS (object_id([TABLE_NAME])),
      PRIMARY KEY CLUSTERED 
      (
           [AUDIT_LOG_TRANSACTION_ID] ASC
      )
    )
    

    Table 2 – holds before/after values.

    CREATE TABLE [dbo].[AUDIT_LOG_DATA](
       [AUDIT_LOG_DATA_ID] [int] IDENTITY(1,1) NOT NULL,
       [AUDIT_LOG_TRANSACTION_ID] [int] NOT NULL,
       [PRIMARY_KEY_DATA] [nvarchar](1500) NOT NULL,
       [COL_NAME] [nvarchar](128) NOT NULL,
       [OLD_VALUE_LONG] [ntext] NULL,
       [NEW_VALUE_LONG] [ntext] NULL,
       [NEW_VALUE_BLOB] [image] NULL,
       [NEW_VALUE]  AS (isnull(CONVERT([varchar](8000),      [NEW_VALUE_LONG],0),CONVERT([varchar](8000),CONVERT([varbinary](8000),substring([NEW_VALUE_BLOB],(1),(8000)),0),0))),
       [OLD_VALUE]  AS (CONVERT([varchar](8000),[OLD_VALUE_LONG],0)),
       [PRIMARY_KEY]  AS ([PRIMARY_KEY_DATA]),
       [DATA_TYPE] [char](1) NOT NULL,
       [KEY1] [nvarchar](500) NULL,
       [KEY2] [nvarchar](500) NULL,
       [KEY3] [nvarchar](500) NULL,
       [KEY4] [nvarchar](500) NULL,
    PRIMARY KEY CLUSTERED 
     (
        [AUDIT_LOG_DATA_ID] ASC
    )
    )
    

    Insert trigger

    I’m not showing triggers for update because they are quite long and have the same logic as this one.

    CREATE TRIGGER [dbo].[tr_i_AUDIT_Audited_Table]
    ON [dbo].[Audited_Table]
    FOR INSERT
    NOT FOR REPLICATION
    As
    BEGIN
    DECLARE 
        @IDENTITY_SAVE              varchar(50),
        @AUDIT_LOG_TRANSACTION_ID       Int,
        @PRIM_KEY               nvarchar(4000),
        @ROWS_COUNT             int
    
    SET NOCOUNT ON
    Select @ROWS_COUNT=count(*) from inserted
    Set @IDENTITY_SAVE = CAST(IsNull(@@IDENTITY,1) AS varchar(50))
    
    INSERT
    INTO dbo.AUDIT_LOG_TRANSACTIONS
    (
        TABLE_NAME,
        TABLE_SCHEMA,
        AUDIT_ACTION_ID,
        HOST_NAME,
        APP_NAME,
        MODIFIED_BY,
        MODIFIED_DATE,
        AFFECTED_ROWS,
        [DATABASE]
    )
    values(
        'Audited_Table',
        'dbo',
        2,  --  ACTION ID For INSERT
        CASE 
          WHEN LEN(HOST_NAME()) < 1 THEN ' '
          ELSE HOST_NAME()
        END,
        CASE 
          WHEN LEN(APP_NAME()) < 1 THEN ' '
          ELSE APP_NAME()
        END,
        SUSER_SNAME(),
        GETDATE(),
        @ROWS_COUNT,
        'Database_Name'
    )
    
    Set @AUDIT_LOG_TRANSACTION_ID = SCOPE_IDENTITY()    
    
    --This INSERT INTO code is repeated for each columns that is audited. 
    --Below are examples for only two columns
    INSERT INTO dbo.AUDIT_LOG_DATA
    (
        AUDIT_LOG_TRANSACTION_ID,
        PRIMARY_KEY_DATA,
        COL_NAME,
        NEW_VALUE_LONG,
        DATA_TYPE
        , KEY1
    )
    SELECT
        @AUDIT_LOG_TRANSACTION_ID,
        convert(nvarchar(1500), IsNull('[PK_Column]='+CONVERT(nvarchar(4000), NEW.[PK_Column], 0), '[PK_Column] Is Null')),
        'Column1',
        CONVERT(nvarchar(4000), NEW.[Column1], 0),
        'A'
        , CONVERT(nvarchar(500), CONVERT(nvarchar(4000), NEW.[PK_Column], 0))
    FROM inserted NEW
    WHERE NEW.[Column1] Is Not Null
    
     --value is inserted for each column that is selected for auditin
    INSERT INTO dbo.AUDIT_LOG_DATA
    (
        AUDIT_LOG_TRANSACTION_ID,
        PRIMARY_KEY_DATA,
        COL_NAME,
        NEW_VALUE_LONG,
        DATA_TYPE
        , KEY1
    )
    SELECT
        @AUDIT_LOG_TRANSACTION_ID,
        convert(nvarchar(1500), IsNull('[PK_Column]='+CONVERT(nvarchar(4000), NEW.[PK_Column], 0), '[PK_Column] Is Null')),
        'Column2',
        CONVERT(nvarchar(4000), NEW.[Column2], 0),
        'A'
        , CONVERT(nvarchar(500), CONVERT(nvarchar(4000), NEW.[PK_Column], 0))
        FROM inserted NEW
        WHERE NEW.[Column2] Is Not Null
    End
    

    Disclaimer: I’m not affiliated with Apex in any way but I do use their tools in my current job.

提交回复
热议问题