经典又复杂的问题SQL问题: 行转列(MySQL)

情景简介

学校里面记录成绩,每个人的选课不一样,而且以后会添加课程,所以不需要把所有课程当作列。数据库grade里面数据如下图,假定每个人姓名都不一样,作为主键。本文以MySQL为基础,其他数据库会有些许语法不同。


数据库数据:

处理后效果:

下面介绍三种方法:

方法一:

1
2
3
4
5
SELECT DISTINCT  a. name ,
( SELECT score FROM grade b WHERE a. name =b. name AND b.course= '语文' ) AS '语文' ,
( SELECT score FROM grade b WHERE a. name =b. name AND b.course= '数学' ) AS '数学' ,
( SELECT score FROM grade b WHERE a. name =b. name AND b.course= '英语' ) AS '英语'
FROM grade a

方法二:

1
2
3
4
5
SELECT name ,
SUM ( CASE  course WHEN  '语文' THEN score END ) AS '语文' ,
SUM ( CASE  course WHEN  '数学' THEN score END ) AS '数学' ,
SUM ( CASE  course WHEN  '英语' THEN score END ) AS '英语'
FROM grade GROUP BY name

方法三:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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();

方法分析:

第一种方法使用了表连接。
第二种使用了分组,对每个分组分别处理。
第三种使用了存储过程,其实是第二种方法的动态化,先计算出所有课程的数量,然后对每个分组进行课程查询。
很明显前两种方法属于硬编码,增加课程后就需要修改SQL。而第三种则没有这种问题。

Note:

MySQL中不能在一个存储过程中删除另一个存储过程,只能调用另一个存储过程
本来想在方法三里面写上:DROP PROCEDURE IF EXISTS sp_count();这是错误的。调试的时候如果写错了,只能手动删除了,也没找到好方法。

参考资料:

  • 重温SQL——行转列,列转行
  • 数据库行转列的sql语句

2013-8-8更新:

方法二还可以使用IF语句。
如下所示:

1
2
3
4
5
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()返回一个数字或字符串值,取决于它被使用的上下文。

此条目由 sang.williams 发表在 Database 分类目录,并贴了 MySQL、procedure、SQL、存储过程、行转列 标签。将固定链接加入收藏夹.

原文链接:行转列

Demo:

DELIMITER &&
CREATE PROCEDURE countByWxCom(in startTime int,in endTime int)
BEGIN
DECLARE lowTime int;
DECLARE highTime int;
set lowTime = startTime;
set highTime = endTime;
#创建临时表
create temporary table countByCategory as(SELECT WX_BaseInfo.comId,WX_Company.comName,category,count(category) AS
count FROM WX_BaseInfo,WX_Company WHERE WX_BaseInfo.comID=WX_Company.comID and reportDate BETWEEN lowTime AND highTime GROUP BY WX_BaseInfo.infoId);
#拼接静态SQL字符串,进行行转列
SET @sqlx = CONCAT("SELECT comId,comName,",
"SUM(IF (category = '微博' , count , 0 ) ) as '微博',",
"SUM(IF (category = '网评文章' , count , 0 ) ) as '网评文章',",
"SUM(IF (category = '跟帖' , count , 0 ) ) as '跟帖 '",
"FROM countByCategory GROUP BY comId");
PREPARE stmt FROM @sqlx;
EXECUTE stmt;
DROP table IF EXISTS  countByCategory;
END
&&
use mangPubSt;
call countByWxCom(20140701,20140801);


mysql> call countByWxCom(20140701,20140801);
+-------+------------------+------+----------+-------+
| comId | comName          | 微博 | 网评文章 | 跟帖  |
+-------+------------------+------+----------+-------+
|     1 | 软件工程学院     |    1 |        4 |     3 |
|     4 | 外国语学院       |    1 |        5 |     0 |
|     5 | 电子电气工程学院 |    1 |        3 |     1 |
|    10 | 音乐学院         |    1 |        1 |     1 |
+-------+------------------+------+----------+-------+

你可能感兴趣的:(web,数据库,sql,mysql,存储过程,行转列)