1,准备测试数据
- USE csdn;
- DROP TABLE IF EXISTS csdn.tb;
- CREATE TABLE tb(`cname` VARCHAR(10),cource VARCHAR(10),score INT) ENGINE=INNODB;
- INSERT INTO tb VALUES('张三','语文',74);
- INSERT INTO tb VALUES('张三','数学',83);
- INSERT INTO tb VALUES('张三','物理',93);
- INSERT INTO tb VALUES('李四','语文',74);
- INSERT INTO tb VALUES('李四','数学',84);
- INSERT INTO tb VALUES('李四','物理',94);
- SELECT * FROM tb;
需要得到的结果是:
+--------------------+--------+--------+--------+-----------+--------------+
| 姓名 | 语文 | 数学 | 物理 | 总成绩 | 平均成绩 |
+--------------------+--------+--------+--------+-----------+--------------+
| 张三 | 74.00 | 83.00 | 93.00 | 250.00 | 83.33 |
| 李四 | 74.00 | 84.00 | 94.00 | 252.00 | 84.00 |
| 总成绩平均数 | 74.00 | 83.50 | 93.50 | 251.00 | 83.67 |
+--------------------+--------+--------+--------+-----------+--------------+
2,利用SUM(IF()) 生成列 + WITH ROLLUP 生成汇总行,并利用 IFNULL将汇总行标题显示为 Total_num
SQL代码块如下:
- SELECT cname AS "姓名",
- SUM(IF(cource="语文",score,0)) AS "语文",
- SUM(IF(cource="数学",score,0)) AS "数学",
- SUM(IF(cource="物理",score,0)) AS "物理",
- SUM(score) AS "总成绩",
- ROUND(AVG(score),2) AS "平均成绩"
- FROM tb
- GROUP BY cname
- UNION ALL
- SELECT
- "总成绩平均数",
- ROUND(AVG(`语文`),2) , ROUND(AVG(`数学`),2), ROUND(AVG(`物理`),2), ROUND(AVG(`总成绩`),2), ROUND(AVG(`平均成绩`),2)
- FROM(
- SELECT "all",cname AS "姓名",
- SUM(IF(cource="语文",score,0)) AS "语文",
- SUM(IF(cource="数学",score,0)) AS "数学",
- SUM(IF(cource="物理",score,0)) AS "物理",
- SUM(score) AS "总成绩",
- AVG(score) AS "平均成绩"
- FROM tb
- GROUP BY cname
- )tb2
- GROUP BY tb2.all;
执行结果正确,如下所示:
+--------------------+--------+--------+--------+-----------+--------------+
| 姓名 | 语文 | 数学 | 物理 | 总成绩 | 平均成绩 |
+--------------------+--------+--------+--------+-----------+--------------+
| 张三 | 74.00 | 83.00 | 93.00 | 250.00 | 83.33 |
| 李四 | 74.00 | 84.00 | 94.00 | 252.00 | 84.00 |
| 总成绩平均数 | 74.00 | 83.50 | 93.50 | 251.00 | 83.67 |
+--------------------+--------+--------+--------+-----------+--------------+
3,利用max(CASE ... WHEN ... THEN .. ELSE END) AS "语文"的方式来实现
SQL代码如下:
- SELECT
- cname AS "姓名",
- MAX(CASE cource WHEN "语文" THEN score ELSE 0 END) AS "语文",
- MAX(CASE cource WHEN "数学" THEN score ELSE 0 END) AS "数学",
- MAX(CASE cource WHEN "物理" THEN score ELSE 0 END) AS "物理",
- SUM(score) AS "总成绩",
- ROUND(AVG(score) ,2) AS "平均成绩"
- FROM tb
- GROUP BY `cname`
- UNION ALL
- SELECT
- "总成绩平均数",
- ROUND(AVG(`语文`),2) , ROUND(AVG(`数学`),2), ROUND(AVG(`物理`),2), ROUND(AVG(`总成绩`),2), ROUND(AVG(`平均成绩`),2)
- FROM( SELECT 'all' ,
- cname AS "姓名",
- MAX(CASE cource WHEN "语文" THEN score ELSE 0 END) AS "语文",
- MAX(CASE cource WHEN "数学" THEN score ELSE 0 END) AS "数学",
- MAX(CASE cource WHEN "物理" THEN score ELSE 0 END) AS "物理",
- SUM(score) AS "总成绩",
- ROUND(AVG(score) ,2) AS "平均成绩"
- FROM tb
- GROUP BY `cname`
- )tb2 GROUP BY tb2.all
执行结果正确,如下所示:
+--------------------+--------+--------+--------+-----------+--------------+
| 姓名 | 语文 | 数学 | 物理 | 总成绩 | 平均成绩 |
+--------------------+--------+--------+--------+-----------+--------------+
| 张三 | 74.00 | 83.00 | 93.00 | 250.00 | 83.33 |
| 李四 | 74.00 | 84.00 | 94.00 | 252.00 | 84.00 |
| 总成绩平均数 | 74.00 | 83.50 | 93.50 | 251.00 | 83.67 |
+--------------------+--------+--------+--------+-----------+--------------+
4,利用 WITH rollup结果不符合
- SELECT IFNULL(cname,'总平均数') AS "姓名",
- MAX(CASE cource WHEN "语文" THEN score ELSE 0 END) AS "语文",
- MAX(CASE cource WHEN "数学" THEN score ELSE 0 END) AS "数学",
- MAX(CASE cource WHEN "物理" THEN score ELSE 0 END) AS "物理",
- ROUND(AVG(score),2) AS "总成绩",
- ROUND(AVG(avg_score),2) AS "平均成绩"
- FROM(
- SELECT
- cname ,
- IFNULL(cource,'total') cource,
- SUM(score) AS score,
- ROUND(AVG(score) ,2) AS avg_score
- FROM tb
- GROUP BY `cname`,cource WITH ROLLUP HAVING cname IS NOT NULL
- )tb2
- GROUP BY tb2.cname WITH ROLLUP;
执行结果不对,总平均数栏目,里面各科比较语文数学的班级平均数不对,如下所示:
- mysql> SELECT IFNULL(cname,'总平均数') AS "姓名",
- -> MAX(CASE cource WHEN "语文" THEN score ELSE 0 END) AS "语文",
- -> MAX(CASE cource WHEN "数学" THEN score ELSE 0 END) AS "数学",
- -> MAX(CASE cource WHEN "物理" THEN score ELSE 0 END) AS "物理",
- -> ROUND(AVG(score),2) AS "总成绩",
- -> ROUND(AVG(avg_score),2) AS "平均成绩"
- -> FROM(
- -> SELECT
- ->
- Display ALL 793 possibilities? (Y OR n)
- -> cname ,
- ->
- Display ALL 793 possibilities? (Y OR n)
- -> IFNULL(cource,'total') cource,
- ->
- Display ALL 793 possibilities? (Y OR n)
- -> SUM(score) AS score,
- ->
- Display ALL 793 possibilities? (Y OR n)
- -> ROUND(AVG(score) ,2) AS avg_score
- -> FROM tb
- -> GROUP BY `cname`,cource WITH ROLLUP HAVING cname IS NOT NULL
- -> )tb2
- -> GROUP BY tb2.cname WITH ROLLUP;
- +--------------+--------+--------+--------+-----------+--------------+
- | 姓名 | 语文 | 数学 | 物理 | 总成绩 | 平均成绩 |
- +--------------+--------+--------+--------+-----------+--------------+
- | 张三 | 74 | 83 | 93 | 125.00 | 83.33 |
- | 李四 | 74 | 84 | 94 | 126.00 | 84.00 |
- | 总平均数 | 74 | 84 | 94 | 125.50 | 83.67 |
- +--------------+--------+--------+--------+-----------+--------------+
- 3 ROWS IN SET, 1 warning (0.00 sec)
- mysql>
总结: WITH rollup中对求列的总数是OK的,但是求列的平均数有偏差,这里场景使用不是恰当。
5,使用动态SQL来实现
SQL代码块如下:
- /*仅仅班级成员部分*/
- SET @a='';
- SELECT @a:=CONCAT(@a,'SUM(IF(cource=\'',cource,'\'',',score,0)) AS ',cource,',') FROM (SELECT DISTINCT cource FROM tb) A;
- SET @a=CONCAT(@a,"ROUND(AVG(score) ,2) AS \"平均成绩\"");
- SET @b=CONCAT('SELECT IFNULL(cname,\'总成绩\'),',LEFT(@a,LENGTH(@a)-1),' ,SUM(score) AS \"总成绩\" FROM tb GROUP BY cname ');
- /*班级成员总计部分**/
- SET @a2="";
- SET @b2=CONCAT('SELECT "all",IFNULL(cname,\'总成绩\'),',LEFT(@a,LENGTH(@a)-1),' ,SUM(score) AS \"总成绩\" FROM tb GROUP BY cname ');
- SELECT @a2:=CONCAT(@a2,'ROUND(AVG(`',cource,'`),2),') FROM (SELECT DISTINCT cource FROM tb) A;
- SET @a2=CONCAT(@a2," ROUND(AVG(`平均成绩`),2),ROUND(AVG(`总成绩`),2) ");
- SET @c=CONCAT("SELECT \"班级平均数\",",LEFT(@a2,LENGTH(@a)-1)," FROM(",@b2,")tb2 GROUP BY tb2.all;");
- SET @d=CONCAT(@b," UNION ALL ",@c);
- PREPARE stmt1 FROM @d;
- EXECUTE stmt1;
查看执行结果如下,已经达到效果:
- mysql> /*仅仅班级成员部分*/
- mysql> SET @a='';
- QUERY OK, 0 ROWS affected (0.00 sec)
- mysql> SELECT @a:=CONCAT(@a,'SUM(IF(cource=\'',cource,'\'',',score,0)) AS ',cource,',') FROM (SELECT DISTINCT cource FROM tb) A;
- +-----------------------------------------------------------------------------------------------------------------------------------+
- | @a:=CONCAT(@a,'SUM(IF(cource=\'',cource,'\'',',score,0)) AS ',cource,',') |
- +-----------------------------------------------------------------------------------------------------------------------------------+
- | SUM(IF(cource='语文',score,0)) AS 语文, |
- | SUM(IF(cource='语文',score,0)) AS 语文,SUM(IF(cource='数学',score,0)) AS 数学, |
- | SUM(IF(cource='语文',score,0)) AS 语文,SUM(IF(cource='数学',score,0)) AS 数学,SUM(IF(cource='物理',score,0)) AS 物理, |
- +-----------------------------------------------------------------------------------------------------------------------------------+
- 3 ROWS IN SET (0.00 sec)
- mysql> SET @a=CONCAT(@a,"ROUND(AVG(score) ,2) AS \"平均成绩\"");
- QUERY OK, 0 ROWS affected (0.00 sec)
- mysql> SET @b=CONCAT('SELECT IFNULL(cname,\'总成绩\'),',LEFT(@a,LENGTH(@a)-1),' ,SUM(score) AS \"总成绩\" FROM tb GROUP BY cname ');
- QUERY OK, 0 ROWS affected (0.00 sec)
- mysql>
- mysql> /*班级成员总计部分**/
- mysql> SET @a2="";
- QUERY OK, 0 ROWS affected (0.00 sec)
- mysql> SET @b2=CONCAT('SELECT "all",IFNULL(cname,\'总成绩\'),',LEFT(@a,LENGTH(@a)-1),' ,SUM(score) AS \"总成绩\" FROM tb GROUP BY cname ');
- QUERY OK, 0 ROWS affected (0.00 sec)
- mysql> SELECT @a2:=CONCAT(@a2,'ROUND(AVG(`',cource,'`),2),') FROM (SELECT DISTINCT cource FROM tb) A;
- +-----------------------------------------------------------------------+
- | @a2:=CONCAT(@a2,'ROUND(AVG(`',cource,'`),2),') |
- +-----------------------------------------------------------------------+
- | ROUND(AVG(`语文`),2), |
- | ROUND(AVG(`语文`),2),ROUND(AVG(`数学`),2), |
- | ROUND(AVG(`语文`),2),ROUND(AVG(`数学`),2),ROUND(AVG(`物理`),2), |
- +-----------------------------------------------------------------------+
- 3 ROWS IN SET (0.00 sec)
- mysql> SET @a2=CONCAT(@a2," ROUND(AVG(`平均成绩`),2),ROUND(AVG(`总成绩`),2) ");
- QUERY OK, 0 ROWS affected (0.00 sec)
- mysql> SET @c=CONCAT("SELECT \"班级平均数\",",LEFT(@a2,LENGTH(@a)-1)," FROM(",@b2,")tb2 GROUP BY tb2.all;");
- QUERY OK, 0 ROWS affected (0.00 sec)
- mysql> SET @d=CONCAT(@b," UNION ALL ",@c);
- QUERY OK, 0 ROWS affected (0.00 sec)
- mysql>
- mysql> PREPARE stmt1 FROM @d;
- QUERY OK, 0 ROWS affected (0.00 sec)
- Statement prepared
- mysql> EXECUTE stmt1;
- +---------------------------+--------+--------+--------+--------------+-----------+
- | IFNULL(cname,'总成绩') | 语文 | 数学 | 物理 | 平均成绩 | 总成绩 |
- +---------------------------+--------+--------+--------+--------------+-----------+
- | 张三 | 74.00 | 83.00 | 93.00 | 83.33 | 250.00 |
- | 李四 | 74.00 | 84.00 | 94.00 | 84.00 | 252.00 |
- | 班级平均数 | 74.00 | 83.50 | 93.50 | 83.67 | 251.00 |
- +---------------------------+--------+--------+--------+--------------+-----------+
- 3 ROWS IN SET (0.00 sec)
- mysql>