Criei uma plataforma de teste simples para testar a Auditoria do SQL Server contra gatilhos e potencialmente outras opções. Nos meus testes de inserção de 1 milhão de linhas em uma tabela, obtive 52, 67 e 159 segundos para a linha de base, a Auditoria SQL e meu gatilho, respectivamente:
Agora, isso não é particularmente científico, mas potencialmente oferece uma maneira de comparar abordagens. Dê uma olhada no script, veja se ele pode ser útil para você:
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
------------------------------------------------------------------------------------------------
Embora a opção de gatilho não tenha se saído muito bem aqui, meu código de gatilho pode ser simplificado dependendo do que você deseja capturar e permite acessar os valores antigos e novos em um formato bastante utilizável que o SQL Audit não faz. Eu usei essa técnica para uma tabela de configuração de atividade mais baixa e funciona muito bem. Dependendo do que você deseja capturar, você também pode considerar o Change Data Capture .
Deixe-me saber como você continua suas tentativas. Boa sorte.