某零售系統記錄每筆交易的「商品分類(DM1)」、「門店(DM2)」、「指標類型(MC1:c1/銷量、c2/客單價、c3/轉換率)」與「數值(VAL)」。管理層需按「分類+門店」維度,將三個指標橫向展開為三欄,供報表直接使用。
CREATE TABLE test (
dm1 CHAR(3),
dm2 CHAR(3),
mc1 NVARCHAR2(20),
val NVARCHAR2(20)
);
INSERT INTO test VALUES ('101','1','c1','100');
INSERT INTO test VALUES ('101','1','c2','80');
INSERT INTO test VALUES ('101','1','c3','40');
INSERT INTO test VALUES ('101','2','c1','30');
INSERT INTO test VALUES ('101','2','c2','80');
INSERT INTO test VALUES ('102','4','c1','9');
INSERT INTO test VALUES ('102','6','c2','50');
COMMIT;
SELECT dm1, dm2,
SUM(DECODE(mc1, 'c1', val)) AS c1,
SUM(DECODE(mc1, 'c2', val)) AS c2,
SUM(DECODE(mc1, 'c3', val)) AS c3
FROM test
GROUP BY dm1, dm2;
輸出:
| DM1 | DM2 | C1 | C2 | C3 |
|---|---|---|---|---|
| 101 | 1 | 100 | 80 | 40 |
| 101 | 2 | 30 | 80 | |
| 102 | 4 | 9 | ||
| 102 | 6 | 50 |
若 VAL 含非數值字元,SUM 會報錯 ORA-01722。此時改用 MAX 同樣可行,因每組僅有一條非 NULL 值:
SELECT dm1, dm2,
MAX(DECODE(mc1, 'c1', val)) AS c1,
MAX(DECODE(mc1, 'c2', val)) AS c2,
MAX(DECODE(mc1, 'c3', val)) AS c3
FROM test
GROUP BY dm1, dm2;
SELECT dm1, dm2,
MAX(CASE WHEN mc1 = 'c1' THEN val END) AS c1,
MAX(CASE WHEN mc1 = 'c2' THEN val END) AS c2,
MAX(CASE WHEN mc1 = 'c3' THEN val END) AS c3
FROM test
GROUP BY dm1, dm2;
邏輯等同 DECODE,符合 ANSI SQL 標準,跨資料庫遷移零修改。
SELECT *
FROM test
PIVOT (
MAX(val) FOR mc1 IN ('c1' AS c1, 'c2' AS c2, 'c3' AS c3)
)
ORDER BY dm1, dm2;
優勢:
PIVOT (SUM(val), COUNT(*) FOR ...)。XML 關鍵字動態產生欄位(適用於未知類別值)。| 方案 | 適用版本 | 可讀性 | 動態欄位 | 備註 |
|---|---|---|---|---|
| DECODE + SUM/MAX | 8i+ | 中 | 否 | 舊系統維護首選 |
| CASE + MAX | 9i+ | 高 | 否 | 跨庫專案推薦 |
| PIVOT | 11g+ | 最高 | 支援 XML | 新專案首選 |
MAX(TO_CHAR(...)) 或 MAX(TO_NUMBER(...)) 顯式轉型。NVL(c1, 0) 或 COALESCE 填補空值。PIVOT XML + XMLTABLE 解析。掌握三種行轉列技巧,可根據版本限制與維護成本靈活選型,讓透視報表開發事半功倍。