行轉列,列轉行是我們在開發過程中經常碰到的問題。
1、行轉列一般通過CASE WHEN 語句來實現
2、也可以通過 SQL SERVER 2005 新增的運算符PIVOT來實現。
用傳統的方法,比較好理解。層次清晰,而且比較習慣。
但是PIVOT 、UNPIVOT提供的語法比一系列復雜的SELECT...CASE 語句中所指定的語法更簡單、更具可讀性。下面我們通過幾個簡單的例子來介紹一下列轉行、行轉列問題。
一、行轉列
我們首先先通過一個老生常談的例子,學生成績表(下面簡化了些)來形象了解下行轉列
CREATE TABLE [StudentScores] ( [UserName] NVARCHAR(20), --學生姓名 [Subject] NVARCHAR(30), --科目 [Score] FLOAT, --成績 )
插入數據

INSERT INTO [StudentScores] SELECT 'Nick', '語文', 80 INSERT INTO [StudentScores] SELECT 'Nick', '數學', 90 INSERT INTO [StudentScores] SELECT 'Nick', '英語', 70 INSERT INTO [StudentScores] SELECT 'Nick', '生物', 85 INSERT INTO [StudentScores] SELECT 'Kent', '語文', 80 INSERT INTO [StudentScores] SELECT 'Kent', '數學', 90 INSERT INTO [StudentScores] SELECT 'Kent', '英語', 70 INSERT INTO [StudentScores] SELECT 'Kent', '生物', 85
如果我想知道每位學生的每科成績,而且每個學生的全部成績排成一行,這樣方便我查看、統計,導出數據
1、case when 方式
SELECT UserName, MAX(CASE Subject WHEN '語文' THEN Score ELSE 0 END) AS '語文', MAX(CASE Subject WHEN '數學' THEN Score ELSE 0 END) AS '數學', MAX(CASE Subject WHEN '英語' THEN Score ELSE 0 END) AS '英語', MAX(CASE Subject WHEN '生物' THEN Score ELSE 0 END) AS '生物' FROM dbo.[StudentScores] GROUP BY UserName
查詢結果如圖所示,這樣我們就能很清楚的了解每位學生所有的成績了
2、PIVOT方式
SELECT * FROM [StudentScores] /*數據源*/ AS P PIVOT ( SUM(Score/*行轉列后 列的值*/) FOR p.Subject/*需要行轉列的列*/ IN ([語文],[數學],[英語],[生物]/*列的值*/) ) AS T
另舉一例做解釋:
SELECT [星期一],[星期二],[星期三],[星期四],[星期五],[星期六],[星期日] --這里是PIVOT第三步(選擇行轉列后的結果集的列)這里可以用“*”表示選擇所有列,也可以只選擇某些列(也就是某些天),通過TBL來定義列的別名 FROM WEEK_INCOME --這里是PIVOT第二步驟(准備原始的查詢結果,因為PIVOT是對一個原始的查詢結果集進行轉換操作,所以先查詢一個結果集出來)這里可以是一個select子查詢, --但為子查詢時候要指定別名,否則語法錯誤 PIVOT ( SUM(INCOME) for [week] in([星期一],[星期二],[星期三],[星期四],[星期五],[星期六],[星期日]) --這里是PIVOT第一步驟,也是核心的地方,進行行轉列操作。聚合函數SUM表示你需要怎樣處理轉換后的列的值,是總和(sum),還是平均(avg)還是min,max等等。 --例如如果week_income表中有兩條數據並且其week都是“星期一”,其中一條的income是1000,另一條income是500,那么在這里使用sum,行轉列后“星期一”這個列的值當然是1500了。 --后面的for [week] in([星期一],[星期二]...)中 for [week]就是說將week列的值分別轉換成一個個列,也就是“以值變列”。 --但是需要轉換成列的值有可能有很多,我們只想取其中幾個值轉換成列,那么怎樣取呢?就是在in里面了,比如我此刻只想看工作日的收入,在in里面就只寫“星期一”至“星期五” --(注意,in里面是原來week列的值,"以值變列")。總的來說,SUM(INCOME) for [week] in([星期一],[星期二],[星期三],[星期四],[星期五],[星期六],[星期日])這句的意思直譯出來, --就是說:將列[week]值為"星期一","星期二","星期三","星期四","星期五","星期六","星期日"分別轉換成列,這些列的值取income的總和。 )TBL --別名一定要寫
二、列轉行
1、測試數據准備

CREATE TABLE ProgrectDetail ( ProgrectName NVARCHAR(20), --工程名稱 OverseaSupply INT, --海外供應商供給數量 NativeSupply INT, --國內供應商供給數量 SouthSupply INT, --南方供應商供給數量 NorthSupply INT --北方供應商供給數量 ) INSERT INTO ProgrectDetail SELECT 'A', 100, 200, 50, 50 UNION ALL SELECT 'B', 200, 300, 150, 150 UNION ALL SELECT 'C', 159, 400, 20, 320 UNION ALL
sql語句:
SELECT P.ProgrectName,P.Supplier,P.SupplyNum FROM ( SELECT ProgrectName, OverseaSupply, NativeSupply, SouthSupply, NorthSupply FROM ProgrectDetail )T UNPIVOT ( SupplyNum FOR Supplier IN (OverseaSupply, NativeSupply, SouthSupply, NorthSupply ) ) P
結果: