經典SQL問題: 行轉列

學校裏面記錄成績,每一個人的選課不同,並且之後會添加課程,因此不須要把全部課程看成列。數據庫grade裏面數據以下圖,假定每一個人姓名都不同,做爲主鍵。本文以MySQL爲基礎,其餘數據庫會有些許語法不一樣。 html

數據庫數據:
grade_table sql

處理後效果:
grade_result 數據庫

下面介紹三種方法: 編碼

方法一: spa

SELECTDISTINCT a.name,
(SELECTscoreFROMgrade bWHEREa.name=b.nameANDb.course='語文')AS'語文',
(SELECTscoreFROMgrade bWHEREa.name=b.nameANDb.course='數學')AS'數學',
(SELECTscoreFROMgrade bWHEREa.name=b.nameANDb.course='英語')AS'英語'
FROMgrade a

方法二: 調試

SELECTname,
SUM(CASE courseWHEN '語文'THENscoreEND)AS'語文',
SUM(CASE courseWHEN '數學'THENscoreEND)AS'數學',
SUM(CASE courseWHEN '英語'THENscoreEND)AS'英語'
FROMgradeGROUPBYname


方法三: code

DELIMITER &&
CREATE PROCEDURE sp_count()
BEGIN
#課程名稱
DECLARE course_n VARCHAR(20);
#全部課程數量
DECLARE count INT;
#計數器
DECLARE i INT DEFAULT 0;
#拼接SQL字符串
SET @s = 'SELECT name';
SET count = (SELECT  COUNT(distinct course) FROM grade);
WHILE i < count DO
SET course_n = (SELECT course FROM grade LIMIT i,1);
SET @s = CONCAT(@s, ', SUM(CASE  course WHEN  ','\'', course_n,'\'',' THEN score END )',' AS ','\'',course_n,'\'');
SET i = i+1;
END WHILE;
SET @s = CONCAT(@s, ' FROM grade GROUP BY name');
#用於調試
#SELECT @s;
PREPARE stmt FROM @s;
EXECUTE stmt;
END
&&
 
call sp_count();



方法分析: htm

第一種方法使用了錶鏈接。
第二種使用了分組,對每一個分組分別處理。
第三種使用了存儲過程,實際上是第二種方法的動態化,先計算出全部課程的數量,而後對每一個分組進行課程查詢。
很明顯前兩種方法屬於硬編碼,增長課程後就須要修改SQL。而第三種則沒有這種問題。 blog

Note: 字符串

MySQL中不能在一個存儲過程當中刪除另外一個存儲過程,只能調用另外一個存儲過程
原本想在方法三裏面寫上:DROP PROCEDURE IF EXISTS sp_count();這是錯誤的。調試的時候若是寫錯了,只能手動刪除了,也沒找到好方法。

參考資料:


2013-8-8更新:

方法二還可使用IF語句。
以下所示:

SELECT name,
SUM(IF (course = '語文' , score , null ) ) as '語文',
SUM(IF (course = '數學' , score , null ) ) as '數學',
SUM(IF (course = '英語' , score , null ) ) as '英語 '
FROM grade GROUP BY name
  • IF(expr1,expr2,expr3),若是expr1是TRUE(expr1<>0且expr1<>NULL),那麼IF()返回expr2,不然它返回expr3。IF()返回一個數字或字符串值,取決於它被使用的上下文。
相關文章
相關標籤/搜索