在資料分析場景中,常需找出「某群組內所有記錄均滿足特定條件」的群組鍵值。本文以「查找狀態全為 1 的 ID」為例,示範如何靈活運用 Oracle 分析函數高效解決此類問題,並對比傳統寫法優缺點。
CREATE TABLE TAB_FXHS (
ID VARCHAR2(32), -- 主鍵 ID(允許重複,代表群組鍵)
ZT VARCHAR2(2) -- 狀態碼
);
COMMENT ON TABLE TAB_FXHS IS '用於統計狀態值全為正常的 ID 值';
COMMENT ON COLUMN TAB_FXHS.ID IS '主鍵 ID';
COMMENT ON COLUMN TAB_FXHS.ZT IS '狀態';
INSERT INTO TAB_FXHS (ID, ZT) VALUES ('10125', '0');
INSERT INTO TAB_FXHS (ID, ZT) VALUES ('10161', '0');
INSERT INTO TAB_FXHS (ID, ZT) VALUES ('10141', '0');
INSERT INTO TAB_FXHS (ID, ZT) VALUES ('10126', '1'); -- 單筆狀態=1
INSERT INTO TAB_FXHS (ID, ZT) VALUES ('10102', '0');
INSERT INTO TAB_FXHS (ID, ZT) VALUES ('10103', '0');
INSERT INTO TAB_FXHS (ID, ZT) VALUES ('10121', '0'); -- ID 10121 混合狀態
INSERT INTO TAB_FXHS (ID, ZT) VALUES ('10121', '1');
INSERT INTO TAB_FXHS (ID, ZT) VALUES ('10121', '0');
INSERT INTO TAB_FXHS (ID, ZT) VALUES ('10121', '3');
COMMIT;
預期結果:僅 10126 應被選出(單筆且狀態=1);10121 因包含非 1 狀態而排除。
TAB_FXHS 中 ID 可重複,ZT 為狀態欄位。COUNT(*) = COUNT(CASE WHEN ZT='1' THEN 1 END) OVER (PARTITION BY ID),或 MIN(ZT) = '1' AND MAX(ZT) = '1'。WITH ZT_PARTITION_BY_ID AS (
SELECT ID, ZT,
COUNT(ZT) OVER (PARTITION BY ID ORDER BY ID) ID_ZT
FROM TAB_FXHS
GROUP BY ID, ZT
)
SELECT *
FROM ZT_PARTITION_BY_ID
WHERE ID_ZT = 1
AND ZT = '1';
問題:原查詢先 GROUP BY ID, ZT 再計算 COUNT(ZT) OVER...,實際統計的是「該 ID 下有幾種不同的 ZT 值」,而非「該 ID 總記錄數」。若某 ID 只有狀態 1,但有多筆記錄,GROUP BY 後僅剩 1 列,COUNT 為 1,恰好通過;但若該 ID 有 3 筆狀態 1,GROUP BY 仍為 1 列,邏輯看似成立,實則語意不清。
WITH ID_STATUS_STATS AS (
SELECT
ID,
ZT,
COUNT(*) OVER (PARTITION BY ID) AS TOTAL_CNT,
COUNT(CASE WHEN ZT = '1' THEN 1 END) OVER (PARTITION BY ID) AS ZT1_CNT
FROM TAB_FXHS
)
SELECT DISTINCT ID
FROM ID_STATUS_STATS
WHERE TOTAL_CNT = ZT1_CNT
AND TOTAL_CNT > 0;
優勢:邏輯直觀、無需自連接、單次掃描、效能優於 NOT EXISTS 或 HAVING 子查詢寫法。
| 寫法 | SQL 範例 | 優點 | 缺點 |
|---|---|---|---|
| 分析函數 | 上文修正版 | 單掃描、可擴展性強、易維護 | 需理解窗口函數語意 |
| HAVING + 聚合 | SELECT ID FROM TAB_FXHS GROUP BY ID HAVING MIN(ZT)='1' AND MAX(ZT)='1' | 語法簡單、直觀 | 大數據量下可能需排序/雜湊分組,資源消耗較大 |
| NOT EXISTS | SELECT DISTINCT ID FROM TAB_FXHS t1 WHERE NOT EXISTS (SELECT 1 FROM TAB_FXHS t2 WHERE t2.ID=t1.ID AND t2.ZT!='1') | 邏輯清晰 | 相關子查詢可能觸發多次掃描 |
(ID, ZT) 建立複合索引,支援索引快速全掃描或範圍掃描。ID 雜湊分區,配合並行查詢加速。分析函數(窗口函數)為「群組內全滿足條件」類問題提供了優雅且高效的解決路徑。關鍵在於:先以窗口聚合計算群組總數與符合條件數,再於外層比對。避免過早 GROUP BY 導致語意偏差。實務上,配合適當索引與統計資訊維護,該模式在千萬級資料下仍能保持亞秒級回應。