Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Code Block
languagesql
titleMoves old rows from a single audit table into a central archive DB and deletes them from the source
collapsetrue
/* Moves old rows from a single audit table into a central archive DB
   and deletes them from the source while keeping the most recent 500 rows.
   Archive table lives in: [@TargetDatabase].[@SourceDatabase].[TableName]
*/
CREATE OR ALTER PROCEDURE dbo._as_SQL_CleanAuditLogs
    @TableName       SYSNAME,
    @TargetDatabase  SYSNAME,            -- e.g. Evo_AuditLogsArchive
    @SourceDatabase  SYSNAME = NULL,     -- defaults to current DB if NULL
    @DaysToKeep      INT    = 14,
    @SkipMail        BIT    = 0
WITH ENCRYPTION
AS
BEGIN
    SET NOCOUNT ON;
 
    IF @SourceDatabase IS NULL
        SET @SourceDatabase = DB_NAME();
 
    DECLARE @srcDB SYSNAME = QUOTENAME(@SourceDatabase);
    DECLARE @tgtDB SYSNAME = QUOTENAME(@TargetDatabase);
    DECLARE @tbl   SYSNAME = QUOTENAME(@TableName);
    DECLARE @sch   SYSNAME = QUOTENAME(@SourceDatabase); -- archive schema per source DB
 
    -- Ensure target schema exists in archive DB
    DECLARE @SQL NVARCHAR(MAX) =
    N'IF NOT EXISTS (SELECT 1 FROM ' + @tgtDB + N'.sys.schemas WHERE name = N''' + REPLACE(@SourceDatabase,'''','''''') + N''')
       EXEC (''' + QUOTENAME(@TargetDatabase) + N'..sp_executesql N''''CREATE SCHEMA ' + @sch + N''''''' );';
    EXEC (@SQL);
	PRINT @SQL
 
    -- Ensure target table exists (shape cloned from source)
    SET @SQL = N'IF OBJECT_ID(''' + @tgtDB + N'.' + @sch + N'.' + @tbl + N''',''U'') IS NULL
    BEGIN
        SELECT TOP 0 * INTO ' + @tgtDB + N'.' + @sch + N'.' + @tbl +
        N' FROM ' + @srcDB + N'.dbo.' + @tbl + N';
    END;';
    EXEC sp_executesql @SQL;
	PRINT @SQL
 
    -- Build filter: move/delete rows older than @DaysToKeep but never touch newest 500
    -- We protect rows whose _auditDate is >= the 500th most-recent _auditDate.
    -- Note: if many rows share the exact same _auditDate as the 500th, all with that same timestamp are protected.
    DECLARE @CutoffExpr NVARCHAR(50) = N'DATEADD(DAY, -' + CAST(@DaysToKeep AS NVARCHAR(12)) + N', GETDATE())';
 
    DECLARE @MoveDeletePredicate NVARCHAR(MAX) = N'
        _auditDate < ' + @CutoffExpr + N'
        AND _auditDate < (
            SELECT MIN(_auditDate) FROM (
                SELECT TOP (500) _auditDate
                FROM ' + @srcDB + N'.dbo.' + @tbl + N'
                ORDER BY _auditDate DESC
            ) d
        )';
 
    -- INSERT to archive
    SET @SQL = N'
        INSERT INTO ' + @tgtDB + N'.' + @sch + N'.' + @tbl + N'
        SELECT *
        FROM ' + @srcDB + N'.dbo.' + @tbl + N'
        WHERE ' + @MoveDeletePredicate + N';';
    EXEC sp_executesql @SQL;
 
    -- DELETE from source
    SET @SQL = N'
        DELETE FROM ' + @srcDB + N'.dbo.' + @tbl + N'
        WHERE ' + @MoveDeletePredicate + N';';
    EXEC sp_executesql @SQL;
	PRINT @SQL
 
    DECLARE @RecordsDeleted INT = @@ROWCOUNT;
 
    -- Optional mail
    IF @SkipMail = 0
    BEGIN
        DECLARE @MailBody    NVARCHAR(400) = N'Table: ' + @TableName + N' (' + @SourceDatabase + N'). Deleted: ' + CAST(@RecordsDeleted AS NVARCHAR(12));
        DECLARE @MailSubject NVARCHAR(400) = N'Audit Cleanup ' + @SourceDatabase + N'..' + @TableName + N' - ' + CAST(@RecordsDeleted AS NVARCHAR(12)) + N' rows';
 
        EXEC msdb.dbo.sp_send_dbmail
            @profile_name    = 'Ambro DB MailAsamcoMailProfile',
            @recipients      = 'alex+AmbroSQL@asamcoSRPSQL@asamco.com',
            @copy_recipients = 'marcelle@ambro.co.za',
            @subject         = @MailSubject,
            @body            = @MailBody;
    END
 
    RETURN @RecordsDeleted;
END
GO


...