Ho creato un semplice banco di prova per provare SQL Server Audit contro i trigger e potenzialmente altre opzioni. Nei miei test per l'inserimento di 1 milione di righe in una tabella ho ottenuto rispettivamente 52, 67 e 159 secondi per la baseline, SQL Audit e il mio trigger:
Ora questo non è particolarmente scientifico ma potenzialmente ti offre un modo per confrontare gli approcci. Dai un'occhiata allo script, vedi se può esserti utile:
USE master
GO
SET NOCOUNT ON
GO
IF EXISTS ( SELECT * FROM sys.databases WHERE name = 'testAuditDb' )
ALTER DATABASE testAuditDb SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
IF EXISTS ( SELECT * FROM sys.databases WHERE name = 'testAuditDb' )
DROP DATABASE testAuditDb
GO
CREATE DATABASE testAuditDb
ON PRIMARY
( NAME = N'testAuditDb', FILENAME = N's:\temp\testAuditDb.mdf', SIZE = 1GB, MAXSIZE = UNLIMITED, FILEGROWTH = 128MB )
LOG ON
( NAME = N'testAuditDb_log', FILENAME = N's:\temp\testAuditDb_log.ldf', SIZE = 100MB, MAXSIZE = 2048GB, FILEGROWTH = 128MB )
GO
ALTER DATABASE testAuditDb SET RECOVERY SIMPLE
GO
------------------------------------------------------------------------------------------------
-- Setup START
------------------------------------------------------------------------------------------------
USE testAuditDb
GO
CREATE SCHEMA auditSchema
-- Create a table
CREATE TABLE auditSchema.auditTable (
rowId INT IDENTITY PRIMARY KEY,
someData UNIQUEIDENTIFIER DEFAULT NEWID(),
dateAdded DATETIME DEFAULT GETDATE(),
addedBy VARCHAR(30) DEFAULT SUSER_NAME(),
ts ROWVERSION
)
GO
-- Setup END
------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------
-- Test 01 - Baseline START
-- Normal timing; no triggers or audits
------------------------------------------------------------------------------------------------
-- Add a million rows to the table and time it.
DECLARE @i INT = 0, @startTime DATETIME2, @endTime DATETIME2
SET @startTime = SYSDATETIME()
WHILE @i < 1000000
BEGIN
INSERT INTO auditSchema.auditTable DEFAULT VALUES
SET @i += 1
END
SET @endTime = SYSDATETIME()
SELECT DATEDIFF( second, @startTime, @endTime ) AS baseline
GO
-- Cleanup
TRUNCATE TABLE auditSchema.auditTable
GO
-- Test 01 - Baseline END
------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------
-- Test 02 - SQL Audit START
-- Try SQL Audit
------------------------------------------------------------------------------------------------
-- Create server audit in master database
USE master
GO
------------------------------------------------------------------------------------------------------------------------
-- The server audit is created with a WHERE clause that limits the server audit to only the auditTable table.
------------------------------------------------------------------------------------------------------------------------
CREATE SERVER AUDIT auditTableAccess TO FILE ( FILEPATH = 'S:\SQLAudit\' ) WHERE object_name = 'auditTable';
GO
ALTER SERVER AUDIT auditTableAccess WITH ( STATE = ON );
GO
-- Create the database audit specification in the testAuditDb database
USE testAuditDb;
GO
CREATE DATABASE AUDIT SPECIFICATION [dbAudit1]
FOR SERVER AUDIT auditTableAccess
ADD (
SELECT, INSERT, UPDATE ON SCHEMA::[auditSchema]
BY [public]
) WITH ( STATE = ON );
GO
-- Add a million rows to the table and time it.
DECLARE @i INT = 0, @startTime DATETIME2, @endTime DATETIME2
SET @startTime = SYSDATETIME()
WHILE @i < 1000000
BEGIN
INSERT INTO auditSchema.auditTable DEFAULT VALUES
SET @i += 1
END
SET @endTime = SYSDATETIME()
SELECT DATEDIFF( second, @startTime, @endTime ) AS sqlAudit
GO
-- Cleanup
TRUNCATE TABLE auditSchema.auditTable
GO
ALTER DATABASE AUDIT SPECIFICATION [dbAudit1] WITH ( STATE = Off );
DROP DATABASE AUDIT SPECIFICATION [dbAudit1]
GO
USE master
ALTER SERVER AUDIT auditTableAccess WITH ( STATE = OFF );
DROP SERVER AUDIT auditTableAccess
GO
/*
-- Inspect the audit output
IF OBJECT_ID('tempdb..#tmp') IS NOT NULL DROP TABLE #tmp
SELECT *
INTO #tmp
FROM fn_get_audit_file ( 'S:\SQLAudit\auditTableAccess_*.sqlaudit', DEFAULT, DEFAULT );
GO
SELECT statement, MIN(event_time), MAX(event_time), COUNT(*) AS records
FROM #tmp
GROUP BY statement
GO
*/
-- Test 02 - SQL Audit END
------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------
-- Test 03 - Triggers START
-- Trial INSERT/UPDATE trigger with log table
------------------------------------------------------------------------------------------------
USE testAuditDb
GO
CREATE TABLE dbo.auditLog
(
auditLogLog INT IDENTITY PRIMARY KEY,
schemaName SYSNAME NOT NULL,
tableName SYSNAME NOT NULL,
dateAdded DATETIME NOT NULL DEFAULT GETDATE(),
addedBy SYSNAME NOT NULL DEFAULT SUSER_NAME(),
auditXML XML
)
GO
-- Generic audit trigger
CREATE TRIGGER trg_dbo__triggerTest ON auditSchema.auditTable
FOR INSERT, UPDATE, DELETE
AS
BEGIN
IF @@rowcount = 0 RETURN
SET NOCOUNT ON
DECLARE @action VARCHAR(10)
IF EXISTS ( SELECT * FROM inserted )
AND EXISTS ( SELECT * FROM deleted )
SET @action = 'UPDATE'
ELSE IF EXISTS ( SELECT * FROM inserted )
SET @action = 'INSERT'
ELSE IF EXISTS ( SELECT * FROM deleted )
SET @action = 'DELETE'
INSERT INTO dbo.auditLog ( schemaName, tableName, auditXML )
SELECT OBJECT_SCHEMA_NAME( parent_id ) schemaName, OBJECT_NAME( parent_id ) tableName,
(
SELECT
@action "@action",
( SELECT 'inserted' source, * FROM inserted FOR XML RAW, TYPE ),
( SELECT 'deleted' source, * FROM deleted FOR XML RAW, TYPE )
FOR XML PATH('mergeOutput'), TYPE
) x
FROM sys.triggers
WHERE OBJECT_ID = @@procid
AND ( EXISTS ( SELECT * FROM inserted )
OR EXISTS ( SELECT * FROM deleted )
)
END
GO
-- Add a million rows to the table and time it.
DECLARE @i INT = 0, @startTime DATETIME2, @endTime DATETIME2
SET @startTime = SYSDATETIME()
WHILE @i < 1000000
BEGIN
INSERT INTO auditSchema.auditTable DEFAULT VALUES
SET @i += 1
END
SET @endTime = SYSDATETIME()
SELECT DATEDIFF( second, @startTime, @endTime ) AS triggers
GO
-- Cleanup
TRUNCATE TABLE auditSchema.auditTable
DROP TABLE dbo.auditLog
DROP TRIGGER auditSchema.trg_dbo__triggerTest
GO
-- Test 03 - Triggers END
------------------------------------------------------------------------------------------------
Mentre l'opzione di trigger non ha funzionato molto bene qui, il mio codice di trigger potrebbe essere semplificato a seconda di ciò che si desidera acquisire e ti consente di accedere ai valori vecchi e nuovi in un formato abbastanza utilizzabile che SQL Audit non fa. Ho usato questa tecnica per una tabella di configurazione a bassa attività e funziona abbastanza bene. A seconda di ciò che desideri acquisire, puoi anche prendere in considerazione Change Data Capture .
Fammi sapere come vai avanti con le tue prove. In bocca al lupo.