情景简介
学校里面记录成绩,每个人的选课不一样,而且以后会添加课程,所以不需要把所有课程当作列。数据库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();这是错误的。调试的时候如果写错了,只能手动删除了,也没找到好方法。
参考资料:
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()返回一个数字或字符串值,取决于它被使用的上下文。