首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> DB2 資料庫 >> DB2 教學

SQL 性能優化實戰:將 FILTER 操作重寫為 UNION ALL 消除執行計劃瓶頸

     編輯:DB2 教學

SQL 性能優化實戰:將 FILTER 操作重寫為 UNION ALL 消除執行計劃瓶頸

在 Oracle 執行計劃中出現 FILTER** 操作往往意味著性能隱患,其行為類似 NESTED LOOPS,會導致重複掃描、邏輯判斷下推失效。本文記錄一次真實優化案例:通過將包含 OR EXISTS 的單條 SQL 拆分為 UNION ALL 兩部分,成功消除 FILTER,將邏輯讀從 10 萬+ 降至 4 千+,耗時從 5 分鐘級壓縮至秒級。

一、問題 SQL 與原始執行計劃

SELECT GGPI.ID, GGPI.PLAN_STATE, ... , GGPI.WORKING
FROM GG_PD_PP_INFO GGPI
WHERE PLAN_STATE IN (50, 60, 70)
  AND ((GGPI.PLAN_BEGIN_DATE >= SYSDATE AND GGPI.PLAN_BEGIN_DATE <= SYSDATE)
       OR (GGPI.PLAN_END_DATE >= SYSDATE AND GGPI.PLAN_END_DATE <= SYSDATE)
       OR (GGPI.PLAN_BEGIN_DATE < SYSDATE AND GGPI.PLAN_END_DATE > SYSDATE))
  AND GGPI.BUREAU_CODE = '0306'
  AND (GGPI.WORK_MASTER_UID = 'FA3502D1291A4A07A3B9E3E0F9A41904'
       OR EXISTS (SELECT 1
                  FROM GG_PD_PP_WORK_MEMBER PMEM
                  WHERE PMEM.PROD_PLAN_ID = GGPI.ID
                    AND PMEM.WORK_MEMBER_UID = 'FA3502D1291A4A07A3B9E3E0F9A41904'
                    AND PMEM.BUREAU_CODE = '0306'));

關鍵計劃特徵:

  • Id 1:FILTER** 操作(核心瓶頸)
  • Id 4:GG_PD_PP_INFO 全表掃描(分區表 PARTITION RANGE ALL)
  • Id 5-6:針對 GG_PD_PP_WORK_MEMBER 的索引範圍掃描 + 表訪問
  • Predicate 1:FILTER ("GGPI"."WORK_MASTER_UID" IS NULL OR EXISTS (...))
  • 統計:103,763 consistent gets,耗時 00:05:09

二、優化思路:邏輯等價拆分

原 WHERE 條件為 A AND (B OR C),其中 C 為 EXISTS 子查詢。利用關係代數分配律:

A AND (B OR C)  ≡  (A AND B)  UNION ALL  (A AND C)

拆分後兩部分互斥(通過 WORK_MASTER_UID IS NOT NULL 隱含),避免重複結果,故用 UNION ALL 而非 UNION,省去去重排序。

三、優化後 SQL

-- 部分 1:主負責人直接匹配
SELECT GGPI.ID, GGPI.PLAN_STATE, ... , GGPI.WORKING
FROM GG_PD_PP_INFO GGPI
WHERE PLAN_STATE IN (50, 60, 70)
  AND ((GGPI.PLAN_BEGIN_DATE >= SYSDATE AND GGPI.PLAN_BEGIN_DATE <= SYSDATE)
       OR (GGPI.PLAN_END_DATE >= SYSDATE AND GGPI.PLAN_END_DATE <= SYSDATE)
       OR (GGPI.PLAN_BEGIN_DATE < SYSDATE AND GGPI.PLAN_END_DATE > SYSDATE))
  AND GGPI.BUREAU_CODE = '0306'
  AND GGPI.WORK_MASTER_UID = 'FA3502D1291A4A07A3B9E3E0F9A41904'

UNION ALL

-- 部分 2:成員表關聯匹配
SELECT GGPI.ID, GGPI.PLAN_STATE, ... , GGPI.WORKING
FROM GG_PD_PP_INFO GGPI
WHERE PLAN_STATE IN (50, 60, 70)
  AND ((GGPI.PLAN_BEGIN_DATE >= SYSDATE AND GGPI.PLAN_BEGIN_DATE <= SYSDATE)
       OR (GGPI.PLAN_END_DATE >= SYSDATE AND GGPI.PLAN_END_DATE <= SYSDATE)
       OR (GGPI.PLAN_BEGIN_DATE < SYSDATE AND GGPI.PLAN_END_DATE > SYSDATE))
  AND GGPI.BUREAU_CODE = '0306'
  AND GGPI.ID IN (SELECT PMEM.PROD_PLAN_ID
                  FROM GG_PD_PP_WORK_MEMBER PMEM
                  WHERE PMEM.WORK_MEMBER_UID = 'FA3502D1291A4A07A3B9E3E0F9A41904'
                    AND PMEM.BUREAU_CODE = '0306');

四、優化後執行計劃對比

新計劃亮點:

  • Id 1:UNION-ALL** 替代 FILTER
  • 分支 1(Id 2-3):走 IND_GGPI_WORK_MASTER_UID 索引範圍掃描 → 表訪問,精準定位主負責人數據;
  • 分支 2(Id 4-10):NESTED LOOPS 驅動表為 GG_PD_PP_WORK_MEMBER(走 IND_GGPW_WORK_MEMBER_UID 索引),再回主表走主鍵 PK_GG_PD_PP_INFO 唯一掃描;
  • 新增 SORT UNIQUE(Id 6)保證成員表去重,避免重複關聯;
  • 統計:4,321 consistent gets,耗時 00:00:45,物理讀 550(首次執行需讀盤)。

五、效果量化

指標優化前優化後提升倍數
Consistent Gets103,7634,32124x
Elapsed Time00:05:0900:00:456.9x
Execution PlanFILTER + 全掃UNION-ALL + 索引驅動質的飛躍

六、核心經驗總結

  1. 識別 FILTER:執行計劃出現 FILTER 且關聯子查詢(EXISTS/IN)時,優先考慮重寫;
  2. 邏輯拆分:將 OR 條件拆分為互斥的 UNION ALL 分支,使優化器能為每分支單獨選擇最優訪問路徑;
  3. 索引配合:確保拆分後的過濾列(如 WORK_MASTER_UID、WORK_MEMBER_UID)有可用索引;
  4. 去重處理:子查詢結果可能重複,IN 子句前加 SORT UNIQUE 或改用 EXISTS;
  5. 統計信息:及時收集表/索引統計信息,幫助優化器準確估算基數。

七、適用場景清單

  • 主表大、子表小,且存在 OR EXISTS / OR IN 模式;
  • 主表有多條件過濾(分區裁剪、索引範圍),原計劃未能下推;
  • 業務邏輯允許 UNION ALL(結果集互斥或可接受去重)。

這次優化再次驗證:SQL 重寫往往比調參數、加 Hint 更本質、更穩健。遇到 FILTER 不妨先嘗試邏輯拆分,往往能收獲意想不到的性能紅利。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved