將「長表」 (Long Format) 轉為「寬表」 (Wide Format) 是報表開發常見需求。標準 SQL 採用 CASE WHEN ... THEN ... END 搭配聚合函數實現,跨資料庫通用。
CREATE TABLE student_score (
name VARCHAR(20),
subject VARCHAR(10), -- 'yw'=語文, 'sx'=數學
score INT
);
INSERT INTO student_score VALUES
('小明', 'yw', 85), ('小明', 'sx', 92),
('小紅', 'yw', 78), ('小紅', 'sx', 88),
('小剛', 'yw', 90), ('小剛', 'sx', 95);
長表結構:
+------+---------+-------+ | name | subject | score | +------+---------+-------+ | 小明 | yw | 85 | | 小明 | sx | 92 | | 小紅 | yw | 78 | | 小紅 | sx | 88 | | 小剛 | yw | 90 | | 小剛 | sx | 95 | +------+---------+-------+
+------+------+------+ | name | yw | sx | +------+------+------+ | 小明 | 85 | 92 | | 小紅 | 78 | 88 | | 小剛 | 90 | 95 | +------+------+------+
SELECT
name,
MAX(CASE WHEN subject = 'yw' THEN score END) AS yw,
MAX(CASE WHEN subject = 'sx' THEN score END) AS sx
FROM student_score
GROUP BY name;

# MySQL 預存程序範例
SELECT GROUP_CONCAT(DISTINCT
CONCAT('MAX(CASE WHEN subject = ''', subject, ''' THEN score END) AS ', subject)
) INTO @sql FROM student_score;
SET @sql = CONCAT('SELECT name, ', @sql, ' FROM student_score GROUP BY name');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SELECT
name,
MAX(CASE WHEN subject='yw' THEN score END) AS yw_score,
MAX(CASE WHEN subject='yw' THEN rank END) AS yw_rank,
MAX(CASE WHEN subject='sx' THEN score END) AS sx_score,
MAX(CASE WHEN subject='sx' THEN rank END) AS sx_rank
FROM student_score_with_rank
GROUP BY name;
SELECT
COALESCE(name, '總平均') AS name,
AVG(CASE WHEN subject='yw' THEN score END) AS yw_avg,
AVG(CASE WHEN subject='sx' THEN score END) AS sx_avg
FROM student_score
GROUP BY name WITH ROLLUP;
| 資料庫 | 語法 | 範例 |
|---|---|---|
| SQL Server | PIVOT | SELECT * FROM (SELECT name, subject, score FROM t) src PIVOT (MAX(score) FOR subject IN ([yw],[sx])) pvt |
| Oracle 11g+ | PIVOT | SELECT * FROM t PIVOT (MAX(score) FOR subject IN ('yw' AS yw, 'sx' AS sx)) |
| PostgreSQL | crosstab (tablefunc) | 需安裝擴充套件,較複雜 |
| MySQL | 無原生 | 使用 CASE 聚合法 (本文方法) |
| Spark SQL | pivot | df.groupBy("name").pivot("subject").max("score") |
| 錯誤寫法 | 問題 | 修正 |
|---|---|---|
CASE WHEN subject='yw' THEN score ELSE 0 END | 分數可能為 0,ELSE 0 會干擾 MAX | 省略 ELSE (預設 NULL),MAX 自動忽略 NULL |
| 使用 SUM 而非 MAX | 若同學生同科目有多筆 (重考),SUM 會加總 | 用 MAX/MIN/AVG 視業務定義 |
| 忘記 GROUP BY | 單行結果,非樞紐 | 必須 GROUP BY 維度欄位 (name) |
(name, subject) 建立複合索引,加速分組與篩選。WITH agg AS (SELECT name, subject, MAX(score) AS score FROM t GROUP BY name, subject) SELECT ...掌握 CASE 聚合法,無需依賴資料庫專有語法,即可在任何支援標準 SQL 的系統實現行轉列。