MySQL 实现行转列SQL

MySQL 实现行转列SQL概述好久没写SQL语句,今天看到问答中的一个问题,拿来研究一下。问题链接:关于Mysql的分级输出问题情景简介学校里面记录成绩,每个人的选课不一样,而且以后会添加课程,所以不需要把所有课程当作列。数据表里面数据如下图,使用姓名+课程作为联合主键(有些需求可能不需要联合主键)。本文以MySQL为基础,其他数据库会有些许语法不同。数据库表数据:处理后的结果(行转列):方法一:这里可以使用Max,也可…

大家好,又见面了,我是你们的朋友全栈君。

概述

好久没写SQL语句,今天看到问答中的一个问题,拿来研究一下。

问题链接:关于Mysql 的分级输出问题

情景简介

学校里面记录成绩,每个人的选课不一样,而且以后会添加课程,所以不需要把所有课程当作列。数据表里面数据如下图,使用姓名+课程作为联合主键(有些需求可能不需要联合主键)。本文以MySQL为基础,其他数据库会有些许语法不同。

数据库表数据:

MySQL 实现行转列SQLMySQL 实现行转列SQL

处理后的结果(行转列):

MySQL 实现行转列SQL
MySQL 实现行转列SQL

方法一:

这里可以使用Max,也可以使用Sum;

注意第二张图,当有学生的某科成绩缺失的时候,输出结果为Null; 

[sql] 
view plain  
copy

  1. SELECT  
  2.     SNAME,  
  3.     MAX(  
  4.         CASE CNAME  
  5.         WHEN ‘JAVA’ THEN  
  6.             SCORE  
  7.         END  
  8.     ) JAVA,  
  9.     MAX(  
  10.         CASE CNAME  
  11.         WHEN ‘mysql’ THEN  
  12.             SCORE  
  13.         END  
  14.     ) mysql  
  15. FROM  
  16.     stdscore  
  17. GROUP BY  
  18.     SNAME;  



可以在第一个Case中加入Else语句解决这个问题:

[sql] 
view plain  
copy

  1. SELECT  
  2.     SNAME,  
  3.     MAX(  
  4.         CASE CNAME  
  5.         WHEN ‘JAVA’ THEN  
  6.             SCORE  
  7.         ELSE  
  8.             0  
  9.         END  
  10.     ) JAVA,  
  11.     MAX(  
  12.         CASE CNAME  
  13.         WHEN ‘mysql’ THEN  
  14.             SCORE  
  15.         ELSE  
  16.             0  
  17.         END  
  18.     ) mysql  
  19. FROM  
  20.     stdscore  
  21. GROUP BY  
  22.     SNAME;  

方法二:

[sql] 
view plain  
copy

  1. SELECT DISTINCT  a.sname,  
  2. (SELECT score FROM stdscore b WHERE a.sname=b.sname AND b.CNAME=‘JAVA’ ) AS ‘JAVA’,  
  3. (SELECT score FROM stdscore b WHERE a.sname=b.sname AND b.CNAME=‘mysql’ ) AS ‘mysql’  
  4. FROM stdscore a  



方法三:

[sql] 
view plain  
copy

  1. DROP PROCEDURE  
  2. IF EXISTS sp_score;  
  3. DELIMITER &&  
  4.   
  5. CREATE PROCEDURE sp_score ()  
  6. BEGIN  
  7.     #课程名称  
  8.     DECLARE  
  9.         cname_n VARCHAR (20) ; #所有课程数量  
  10.         DECLARE  
  11.             count INT ; #计数器  
  12.             DECLARE  
  13.                 i INT DEFAULT 0 ; #拼接SQL字符串  
  14.             SET @s = ‘SELECT sname’ ;  
  15.             SET count = (  
  16.                 SELECT  
  17.                     COUNT(DISTINCT cname)  
  18.                 FROM  
  19.                     stdscore  
  20.             ) ;  
  21.             WHILE i < count DO  
  22.   
  23.   
  24.             SET cname_n = (  
  25.                 SELECT  
  26.                     cname  
  27.                 FROM  
  28.                     stdscore  
  29.                 GROUP BY CNAME   
  30.                 LIMIT i,  
  31.                 1  
  32.             ) ;  
  33.             SET @s = CONCAT(  
  34.                 @s,  
  35.                 ‘, SUM(CASE cname WHEN ‘,  
  36.                 ‘\”,  
  37.                 cname_n,  
  38.                 ‘\”,  
  39.                 ‘ THEN score ELSE 0 END)’,  
  40.                 ‘ AS ‘,  
  41.                 ‘\”,  
  42.                 cname_n,  
  43.                 ‘\”  
  44.             ) ;  
  45.             SET i = i + 1 ;  
  46.             END  
  47.             WHILE ;  
  48.             SET @s = CONCAT(  
  49.                 @s,  
  50.                 ‘ FROM stdscore GROUP BY sname’  
  51.             ) ; #用于调试  
  52.             #SELECT @s;  
  53.             PREPARE stmt  
  54.             FROM  
  55.                 @s ; EXECUTE stmt ;  
  56.             END&&  
  57.   
  58. CALL sp_score () ;  


处理后的结果(行转列)分级输出:

MySQL 实现行转列SQL
MySQL 实现行转列SQL




方法一:

这里可以使用Max,也可以使用Sum;

注意第二张图,当有学生的某科成绩缺失的时候,输出结果为Null; 

[sql] 
view plain  
copy

  1. SELECT  
  2.     SNAME,  
  3.     MAX(  
  4.         CASE CNAME  
  5.         WHEN ‘JAVA’ THEN  
  6.             (  
  7.                 CASE  
  8.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) > 20 THEN  
  9.                     ‘优秀’  
  10.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) > 10 THEN  
  11.                     ‘良好’  
  12.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) >= 0 THEN  
  13.                     ‘普通’  
  14.                 ELSE  
  15.                     ‘较差’  
  16.                 END  
  17.             )  
  18.         END  
  19.     ) JAVA,  
  20.     MAX(  
  21.         CASE CNAME  
  22.         WHEN ‘mysql’ THEN  
  23.             (  
  24.                 CASE  
  25.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) > 20 THEN  
  26.                     ‘优秀’  
  27.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) > 10 THEN  
  28.                     ‘良好’  
  29.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) >= 0 THEN  
  30.                     ‘普通’  
  31.                 ELSE  
  32.                     ‘较差’  
  33.                 END  
  34.             )  
  35.         END  
  36.     ) mysql  
  37. FROM  
  38.     stdscore  
  39. GROUP BY  
  40.     SNAME;  



方法二:

[sql] 
view plain  
copy

  1. SELECT DISTINCT  a.sname,  
  2. (SELECT (  
  3.                 CASE  
  4.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) > 20 THEN  
  5.                     ‘优秀’  
  6.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) > 10 THEN  
  7.                     ‘良好’  
  8.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) >= 0 THEN  
  9.                     ‘普通’  
  10.                 ELSE  
  11.                     ‘较差’  
  12.                 END  
  13.             ) FROM stdscore b WHERE a.sname=b.sname AND b.CNAME=‘JAVA’ ) AS ‘JAVA’,  
  14. (SELECT (  
  15.                 CASE  
  16.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) > 20 THEN  
  17.                     ‘优秀’  
  18.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) > 10 THEN  
  19.                     ‘良好’  
  20.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) >= 0 THEN  
  21.                     ‘普通’  
  22.                 ELSE  
  23.                     ‘较差’  
  24.                 END  
  25.             ) FROM stdscore b WHERE a.sname=b.sname AND b.CNAME=‘mysql’ ) AS ‘mysql’  
  26. FROM stdscore a  



方法三:

[sql] 
view plain  
copy

  1. DROP PROCEDURE  
  2. IF EXISTS sp_score;  
  3. DELIMITER &&  
  4.   
  5. CREATE PROCEDURE sp_score ()  
  6. BEGIN  
  7.     #课程名称  
  8.     DECLARE  
  9.         cname_n VARCHAR (20) ; #所有课程数量  
  10.         DECLARE  
  11.             count INT ; #计数器  
  12.             DECLARE  
  13.                 i INT DEFAULT 0 ; #拼接SQL字符串  
  14.             SET @s = ‘SELECT sname’ ;  
  15.             SET count = (  
  16.                 SELECT  
  17.                     COUNT(DISTINCT cname)  
  18.                 FROM  
  19.                     stdscore  
  20.             ) ;  
  21.             WHILE i < count DO  
  22.   
  23.   
  24.             SET cname_n = (  
  25.                 SELECT  
  26.                     cname  
  27.                 FROM  
  28.                     stdscore  
  29.         GROUP BY CNAME   
  30.                 LIMIT i, 1  
  31.             ) ;  
  32.             SET @s = CONCAT(  
  33.                 @s,  
  34.                 ‘, MAX(CASE cname WHEN ‘,  
  35.                 ‘\”,  
  36.                 cname_n,  
  37.                 ‘\”,  
  38.                 ‘ THEN (  
  39.                 CASE  
  40.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=\,cname_n,‘\’) > 20 THEN  
  41.                     \‘优秀\’  
  42.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=\,cname_n,‘\’) > 10 THEN  
  43.                     \‘良好\’  
  44.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=\,cname_n,‘\’) >= 0 THEN  
  45.                     \‘普通\’  
  46.                 ELSE  
  47.                     \‘较差\’  
  48.                 END  
  49.             ) END)’,  
  50.                 ‘ AS ‘,  
  51.                 ‘\”,  
  52.                 cname_n,  
  53.                 ‘\”  
  54.             ) ;  
  55.             SET i = i + 1 ;  
  56.             END  
  57.             WHILE ;  
  58.             SET @s = CONCAT(  
  59.                 @s,  
  60.                 ‘ FROM stdscore GROUP BY sname’  
  61.             ) ;   
  62.             #用于调试  
  63.             #SELECT @s;  
  64.             PREPARE stmt  
  65.             FROM  
  66.                 @s ; EXECUTE stmt ;  
  67.             END&&  
  68.   
  69.   
  70. CALL sp_score ();  

几种方法比较分析

第一种使用了分组,对每个课程分别处理。

第二种方法使用了表连接。

第三种使用了存储过程,实际上可以是第一种或第二种方法的动态化,先计算出所有课程的数量,然后对每个分组进行课程查询。
这种方法的一个最大的好处是当新增了一门课程时,SQL语句不需要重写。

小结

关于行转列和列转行

这个概念似乎容易弄混,有人把行转列理解为列转行,有人把列转行理解为行转列;

这里做个定义:

行转列:把表中特定列(如本文中的:CNAME)的数据去重后做为列名(如查询结果行中的“Java,mysql”,处理后是做为列名输出);

列转行:可以说是行转列的反转,把表中特定列(如本文处理结果中的列名“JAVA,mysql”)做为每一行数据对应列“CNAME”的值;

关于效率

不知道有什么好的生成模拟数据的方法或工具,麻烦小伙伴推荐一下,抽空我做一下对比;

还有其它更好的方法吗?

本文使用的几种方法应该都有优化的空间,特别是使用存储过程的话会更加灵活,功能更强大;

本文的分级只是给出一种思路,分级的方法如果学生的成绩相差较小的话将失去意义;

如果小伙伴有更好的方法,还请不吝赐教,感激不尽!

有些需求可能不需要联合主键

有些需求可能不需要联合主键,因为一门课程可能允许学生考多次,取最好的一次成绩,或者取多次的平均成绩。


原文地址:
http://blog.csdn.net/testcs_dn/article/details/49847299

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/132379.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 详解 & 0xff 的意义及作用

    详解 & 0xff 的意义及作用首先我们要都知道,&表示按位与,只有两个位同时为1,才能得到1,0x代表16进制数,0xff表示的数二进制11111111占一个字节.和其进行&操作的数,最低8位,不会发生变化.下面着重来说说&0xff都有哪些应用:1.只是为了取得低八位通常配合移位操作符>>使用例如:javasocket通信中基于长度的成帧方法中,如果发送的信息长度小于65535字节,长度信息的字节定义为两个字节长度。这时候将两个字节长的长度信息,以Big-Endian的

    2022年6月19日
    48
  • winexec for linux[通俗易懂]

    winexec for linux[通俗易懂]ViewFullVersion:[allvariants]psexecforLinuxpromodusAugust25th,2008,04:42AMAftersometrial&errorI’vegottenwinexectoworkonLinux.Thisisidenticaltothepse

    2022年7月27日
    4
  • Qt QListWidget详解

    Qt QListWidget详解1.QListWidget和QListViewQListWidget是继承QListView,QListView是基于Model的,需要自己来建模(如建立QStringListModel,QSqlTableModel等),保存数据,这样就大大降低了数据冗余,提高了程序的效率,但是需要我们对数据建模有一定的了解,而QListWidget是一个升级版本的QListView,它已经为我们建立了一个数据存储模型QListWidgetItem,操作方便,直接调用addItem即可添加Item项。2….

    2022年6月6日
    109
  • Mybatis模糊查询的四种方式

    Mybatis模糊查询的四种方式Mybatis 模糊查询的四种方式 1 根据姓名模糊查询员工信息 1 1 方式一步骤一 编写配置文件步骤二 测试步骤三 分析此种方式需要在调用处手动的去添加 通配符 1 2 方式二说明 使用方式一可以实现模糊查询 但是有一点不方便的地方就是 在测试类中 调用 selectList 方法传参时需要调用者手动的添加 号通配符 显然是麻烦的 能否在映射配置文件中直接将 号写好呢 有的朋友可能会这么想 好办 直接在配置文件中这么写 形如 1 测试后发现 程序会报错 原因是 缺少单引号 这个

    2025年7月24日
    0
  • datagrip2021.12.4激活【2021.10最新】

    (datagrip2021.12.4激活)最近有小伙伴私信我,问我这边有没有免费的intellijIdea的激活码,然后我将全栈君台教程分享给他了。激活成功之后他一直表示感谢,哈哈~https://javaforall.net/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~4D5U…

    2022年3月30日
    82
  • 51单片机流水灯电路以及C代码「建议收藏」

    51单片机流水灯电路以及C代码「建议收藏」流水灯是51单片机的入门级实验,以下是其电路图以及C代码流水灯proteus电路图此图发光二极管采用共阳极式连接流水灯C代码#include&amp;amp;amp;amp;amp;amp;amp;amp;lt;reg51.h&amp;amp;amp;amp;amp;amp;amp;amp;gt;voiddelay1s(unsignedcharn);voidMovinglight(){ unsignedcharcodeMovinglightA

    2022年5月3日
    61

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号