縱向儲存:每列一筆 (學生, 科目, 分數)。需橫向報表:每列一位學生,科目變欄位。
CREATE TABLE RowTest (
[Name] NVARCHAR(10),
[Course] NVARCHAR(10),
[Record] INT
);
INSERT INTO RowTest VALUES
(N'張三',N'語文',91),(N'張三',N'數學',92),(N'張三',N'英語',93),
(N'張三',N'生物',94),(N'張三',N'物理',95),(N'張三',N'化學',96),
(N'李四',N'語文',81),(N'李四',N'數學',82),(N'李四',N'英語',83),
(N'李四',N'生物',84),(N'李四',N'物理',85),(N'李四',N'化學',86),
(N'小生',N'語文',71),(N'小生',N'數學',72),(N'小生',N'英語',73),
(N'小生',N'生物',74),(N'小生',N'物理',75),(N'小生',N'化學',76);
SELECT [Name], [語文], [數學], [英語], [生物], [物理], [化學]
FROM RowTest
PIVOT (SUM([Record]) FOR [Course] IN ([語文],[數學],[英語],[生物],[物理],[化學])) AS PVT;
DECLARE @sql NVARCHAR(MAX) = N'SELECT [Name]';
SELECT @sql += N', ISNULL(MAX(CASE WHEN [Course] = ' + QUOTENAME([Course], '''') + N' THEN [Record] END), 0) AS ' + QUOTENAME([Course])
FROM (SELECT DISTINCT [Course] FROM RowTest) AS C;
SET @sql += N' FROM RowTest GROUP BY [Name] ORDER BY [Name]';
PRINT @sql; -- 除錯用
EXEC sp_executesql @sql;
原理:先查詢所有唯一科目,動態拼裝 CASE WHEN ... THEN ... END 聚合運算式,再 GROUP BY Name 合併同一學生的多行。ISNULL(...,0) 處理缺考顯示 0 分。
MAX 或 SUM 聚合,否則報錯「子查詢傳回多個值」。橫向表 CoulumTest(Name, 語文, 數學, 英語) 需轉為縱向 (Name, Course, Score) 便於統計分析。
CREATE TABLE CoulumTest (
Name NVARCHAR(10), 語文 INT, 數學 INT, 英語 INT
);
INSERT INTO CoulumTest VALUES (N'張三',90,91,92), (N'李四',80,81,82);
SELECT CT.Name,
Col.name AS 課程,
CASE Col.name
WHEN N'語文' THEN CT.語文
WHEN N'數學' THEN CT.數學
WHEN N'英語' THEN CT.英語
END AS 分數
FROM CoulumTest CT
LEFT JOIN (
SELECT name FROM sys.columns WHERE object_id = OBJECT_ID('CoulumTest')
) Col ON Col.name <> 'Name';
優點:無需 UNION ALL 手寫每欄,利用系統目錄自動獲取需轉換的欄位清單。
缺點:仍需在 CASE WHEN 硬編碼欄位對應值,無法完全通用。
SELECT Name, Course, Score
FROM CoulumTest
UNPIVOT (Score FOR Course IN ([語文], [數學], [英語])) AS UP;
若欄位動態,可結合動態 SQL 組裝 IN 清單。
-- 縱向 → 橫向 → 再轉回縱向
SELECT Name, Course, Score
FROM (
SELECT [Name], [語文], [數學], [英語], [生物], [物理], [化學]
FROM RowTest
PIVOT (SUM(Record) FOR Course IN ([語文],[數學],[英語],[生物],[物理],[化學])) AS PVT
) P
UNPIVOT (Score FOR Course IN ([語文],[數學],[英語],[生物],[物理],[化學])) AS UNPVT;
| 方法 | 適用場景 | 效能 | 維護性 |
|---|---|---|---|
| 靜態 PIVOT/UNPIVOT | 欄位固定、報表固定 | 最佳(查詢計劃可重用) | 高 |
| 動態 SQL + CASE 聚合 | 行轉列、欄位不固定 | 良好(單次掃描) | 中(需動態 SQL) |
| 動態 SQL + UNPIVOT | 列轉行、欄位不固定 | 良好 | 中 |
| 應用層轉換 | 複雜邏輯、多格式輸出 | 移至應用層 | 最高 |
SQL Server 2005 引入 PIVOT/UNPIVOT 關鍵字,配合動態 SQL 可解決絕大多數行列轉換需求。行轉列核心是「聚合 + 分組」,列轉行核心是「將欄位值轉為行值」。選擇靜態或動態取決於欄位結構是否穩定;極複雜報表建議在應用層處理,資料庫專注於關聯查詢與聚合。