SQL SERVER通用分批删除存储过程


  -- 过程参数调用方法


EXEC dbo.usp_DeleteBatchByCondition
    @TableName = 'TBLESIItemLogLot',
    @WhereClause = 'MaxInTime < ''2025-01-01''',
    @BatchSize = 5000,
    @MaxTotalDeletes = 500000;
    @DelayBetweenBatches = '00:00:01';

-- 字符串中的单引号需要双写(''2025-01-01''),这是 T-SQL 字符串规则。


-- 通用分批删除存储过程

CREATE PROCEDURE dbo.usp_DeleteBatchByCondition
    @TableName NVARCHAR(256),             -- 表名,格式:[schema].[table] 或 [table]
    @WhereClause NVARCHAR(MAX),           -- WHERE 条件(不含 "WHERE" 关键字,例如 "MaxInTime < ''2025-01-01''")
    @BatchSize INT = 5000,                -- 每批删除行数
    @MaxTotalDeletes BIGINT = NULL,       -- 最大总删除行数(可选)
    @DelayBetweenBatches VARCHAR(12) = '00:00:03' -- 批次间隔
AS
BEGIN
    SET NOCOUNT ON;

    -- 参数校验
    IF @BatchSize <= 0 
    BEGIN
        RAISERROR('Parameter @BatchSize must be greater than 0.', 16, 1);
        RETURN;
    END

    IF NULLIF(LTRIM(RTRIM(@TableName)), '') IS NULL
    BEGIN
        RAISERROR('Parameter @TableName cannot be empty.', 16, 1);
        RETURN;
    END

    IF NULLIF(LTRIM(RTRIM(@WhereClause)), '') IS NULL
    BEGIN
        RAISERROR('Parameter @WhereClause cannot be empty.', 16, 1);
        RETURN;
    END

    -- 验证表是否存在(防止误删或注入)
    DECLARE @FullTableName NVARCHAR(256);
    DECLARE @ObjectId INT;

    -- 尝试解析为两部分(schema.table)或单部分(table)
    IF CHARINDEX('.', @TableName) > 0
    BEGIN
        SET @FullTableName = QUOTENAME(PARSENAME(@TableName, 2)) + '.' + QUOTENAME(PARSENAME(@TableName, 1));
    END
    ELSE
    BEGIN
        SET @FullTableName = QUOTENAME(@TableName);
    END

    SET @ObjectId = OBJECT_ID(@FullTableName, 'U');
    IF @ObjectId IS NULL
    BEGIN
        RAISERROR('Table [%s] does not exist or is not a user table.', 16, 1, @TableName);
        RETURN;
    END

    -- 构建动态 SQL 模板(注意:@WhereClause 必须由调用方保证安全!)
    DECLARE @Sql NVARCHAR(MAX);
    DECLARE @RowsDeleted INT = 1;
    DECLARE @TotalDeleted BIGINT = 0;

    -- 提示信息
    PRINT 'Starting batch delete on table: ' + @FullTableName;
    PRINT 'Using condition: ' + @WhereClause;

    WHILE @RowsDeleted > 0
    BEGIN
        BEGIN TRY
            BEGIN TRANSACTION;

            -- 构造 DELETE 语句
            SET @Sql = N'DELETE TOP (' + CAST(@BatchSize AS NVARCHAR(10)) + N') FROM ' 
                     + @FullTableName 
                     + N' WHERE ' + @WhereClause + N';';

            -- 执行动态删除
            EXEC sp_executesql @Sql;

            SET @RowsDeleted = @@ROWCOUNT;
            SET @TotalDeleted += @RowsDeleted;

            COMMIT TRANSACTION;

            -- 检查最大删除限制
            IF @MaxTotalDeletes IS NOT NULL AND @TotalDeleted >= @MaxTotalDeletes
            BEGIN
                PRINT 'Reached MaxTotalDeletes limit (' + CAST(@MaxTotalDeletes AS VARCHAR) + '). Stopping.';
                BREAK;
            END

            -- 延迟与进度
            IF @RowsDeleted > 0
            BEGIN
                PRINT CAST(@RowsDeleted AS VARCHAR) + ' rows deleted in this batch. Total so far: ' + CAST(@TotalDeleted AS VARCHAR);
                WAITFOR DELAY @DelayBetweenBatches;
            END

        END TRY
        BEGIN CATCH
            ROLLBACK TRANSACTION;
            DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE();
            DECLARE @ErrorSeverity INT = ERROR_SEVERITY();
            DECLARE @ErrorState INT = ERROR_STATE();
            RAISERROR('Error during batch delete: %s', @ErrorSeverity, @ErrorState, @ErrorMessage);
            RETURN;
        END CATCH
    END

    PRINT 'Deletion completed. Total rows deleted from ' + @FullTableName + ': ' + CAST(@TotalDeleted AS VARCHAR);
END


白俊遥博客
请先登录后发表评论
  • 最新评论
  • 总共0条评论