直接 DELETE FROM big_table WHERE condition 會導致:
核心思想:小批量、高頻次、獨立提交,將大事務拆解為眾多微事務。
CREATE OR REPLACE PROCEDURE del_big_table (
p_table_name IN VARCHAR2, -- 目標表名
p_condition IN VARCHAR2, -- 刪除條件(不含 WHERE)
p_batch_size IN NUMBER -- 每批次行數
) AS
PRAGMA AUTONOMOUS_TRANSACTION; -- 關鍵:每次循環獨立提交
v_deleted_cnt NUMBER := 0;
v_total_cnt NUMBER := 0;
BEGIN
LOOP
EXECUTE IMMEDIATE
'DELETE FROM ' || p_table_name ||
' WHERE ' || p_condition || ' AND ROWNUM <= :bn'
USING p_batch_size;
v_deleted_cnt := SQL%ROWCOUNT;
v_total_cnt := v_total_cnt + v_deleted_cnt;
COMMIT; -- 釋放鎖、UNDO 空間
EXIT WHEN v_deleted_cnt = 0; -- 無數據可刪則退出
-- 可選:短暫休眠降低系統壓力
-- DBMS_LOCK.SLEEP(0.1);
END LOOP;
DBMS_OUTPUT.PUT_LINE('完成!共刪除 ' || v_total_cnt || ' 條記錄。');
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
DBMS_OUTPUT.PUT_LINE('錯誤: ' || SQLERRM);
RAISE;
END;
/
-- 刪除 30 天前的日誌,每次 5000 行
BEGIN
del_big_table('SYS_LOG', 'CREATE_TIME < SYSDATE - 30', 5000);
END;
/
-- 或直接在 SQL*Plus 執行
EXEC del_big_table('ORDERS_HIST', 'STATUS = ''CANCELLED''', 10000);
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| 分批 DELETE (本方案) | 通用性強、無需停機、代碼簡單 | 產生大量 REDO、索引碎片化 | 在線業務表、無維護窗口 |
| 分區表 DROP/TRUNCATE PARTITION | 秒級完成、零 REDO、無碎片 | 需預先按時間/範圍分區 | 數據倉庫、日誌表、歷史數據 |
| CTAS 重建法 | 順序讀寫快、自動重建索引 | 需鎖表、臨時空間大 | 刪除比例 > 50%、有維護窗口 |
| DBMS_REDEFINITION | 在線重定義、業務零中斷 | 實現複雜、限制多 | 7x24 核心業務表結構變更 |
UNUSABLE,結束後 REBUILD,避免索引維護開銷。PARALLEL (DEGREE 4),配合分批邏輯。V$SESSION_LONGOPS、V$UNDOSTAT 確保 UNDO 穩定。ALTER TABLE ... ENABLE ROW MOVEMENT; ALTER TABLE ... SHRINK SPACE; 釋放空間。雖原文測試 FORALL 無優勢,但對於「已知主鍵 ID 列表」的場景,FORALL 批量綁定仍有價值:
TYPE id_tab_t IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
v_ids id_tab_t;
-- 填充 v_ids ...
FORALL i IN INDICES OF v_ids
DELETE FROM target_table WHERE id = v_ids(i);
總結: 分批刪除配合自治事務是通用性最強的「無停機瘦身術」。根據表大小、業務窗口、分區架構靈活選型,才是資深 DBA 的必修功課。