MySQL 行转列

本文介绍了如何在MySQL中将成绩数据从行转换为列的三种方法,包括使用CASE语句结合MAX或SUM,子查询以及创建存储过程。每种方法都考虑了成绩缺失的情况,并提供了根据平均分评估成绩等级的实现。此外,文章讨论了这些方法在处理不断增长的课程数据时的灵活性。
摘要由CSDN通过智能技术生成

一般情况用第一种就可以了

情景简介

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

数据库表数据:

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

 

方法一:

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

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

  1. SELECT  
        SNAME,  
        MAX(  
            CASE CNAME  
            WHEN 'JAVA' THEN  
                SCORE  
            END  
        ) JAVA,  
        MAX(  
            CASE CNAME  
            WHEN 'mysql' THEN  
                SCORE  
            END  
        ) mysql  
    FROM  
        stdscore  
    GROUP BY  
        SNAME; 

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

  1. SELECT  
        SNAME,  
        MAX(  
            CASE CNAME  
            WHEN 'JAVA' THEN  
                SCORE  
            ELSE  
                0  
            END  
        ) JAVA,  
        MAX(  
            CASE CNAME  
            WHEN 'mysql' THEN  
                SCORE  
            ELSE  
                0  
            END  
        ) mysql  
    FROM  
        stdscore  
    GROUP BY  
        SNAME; 
     

方法二:

  1. SELECT DISTINCT  a.sname,  
    (SELECT score FROM stdscore b WHERE a.sname=b.sname AND b.CNAME='JAVA' ) AS 'JAVA',  
    (SELECT score FROM stdscore b WHERE a.sname=b.sname AND b.CNAME='mysql' ) AS 'mysql'  
    FROM stdscore a  


方法三:

  1. DROP PROCEDURE  
    IF EXISTS sp_score;  
    DELIMITER &&  
      
    CREATE PROCEDURE sp_score ()  
    BEGIN  
        #课程名称  
        DECLARE  
            cname_n VARCHAR (20) ; #所有课程数量  
            DECLARE  
                count INT ; #计数器  
                DECLARE  
                    i INT DEFAULT 0 ; #拼接SQL字符串  
                SET @s = 'SELECT sname' ;  
                SET count = (  
                    SELECT  
                        COUNT(DISTINCT cname)  
                    FROM  
                        stdscore  
                ) ;  
                WHILE i < count DO  
      
      
                SET cname_n = (  
                    SELECT  
                        cname  
                    FROM  
                        stdscore  
                    GROUP BY CNAME   
                    LIMIT i,  
                    1  
                ) ;  
                SET @s = CONCAT(  
                    @s,  
                    ', SUM(CASE cname WHEN ',  
                    '\'',  
                    cname_n,  
                    '\'',  
                    ' THEN score ELSE 0 END)',  
                    ' AS ',  
                    '\'',  
                    cname_n,  
                    '\''  
                ) ;  
                SET i = i + 1 ;  
                END  
                WHILE ;  
                SET @s = CONCAT(  
                    @s,  
                    ' FROM stdscore GROUP BY sname'  
                ) ; #用于调试  
                #SELECT @s;  
                PREPARE stmt  
                FROM  
                    @s ; EXECUTE stmt ;  
                END&&  
      
    CALL sp_score () ;  

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

 
 

方法一:

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

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

  1. SELECT  
        SNAME,  
        MAX(  
            CASE CNAME  
            WHEN 'JAVA' THEN  
                (  
                    CASE  
                    WHEN SCORE - (select avg(SCORE) from stdscore where CNAME='JAVA') > 20 THEN  
                        '优秀'  
                    WHEN SCORE - (select avg(SCORE) from stdscore where CNAME='JAVA') > 10 THEN  
                        '良好'  
                    WHEN SCORE - (select avg(SCORE) from stdscore where CNAME='JAVA') >= 0 THEN  
                        '普通'  
                    ELSE  
                        '较差'  
                    END  
                )  
            END  
        ) JAVA,  
        MAX(  
            CASE CNAME  
            WHEN 'mysql' THEN  
                (  
                    CASE  
                    WHEN SCORE - (select avg(SCORE) from stdscore where CNAME='JAVA') > 20 THEN  
                        '优秀'  
                    WHEN SCORE - (select avg(SCORE) from stdscore where CNAME='JAVA') > 10 THEN  
                        '良好'  
                    WHEN SCORE - (select avg(SCORE) from stdscore where CNAME='JAVA') >= 0 THEN  
                        '普通'  
                    ELSE  
                        '较差'  
                    END  
                )  
            END  
        ) mysql  
    FROM  
        stdscore  
    GROUP BY  
        SNAME;  

方法二:

  1. SELECT DISTINCT  a.sname,  
    (SELECT (  
                    CASE  
                    WHEN SCORE - (select avg(SCORE) from stdscore where CNAME='JAVA') > 20 THEN  
                        '优秀'  
                    WHEN SCORE - (select avg(SCORE) from stdscore where CNAME='JAVA') > 10 THEN  
                        '良好'  
                    WHEN SCORE - (select avg(SCORE) from stdscore where CNAME='JAVA') >= 0 THEN  
                        '普通'  
                    ELSE  
                        '较差'  
                    END  
                ) FROM stdscore b WHERE a.sname=b.sname AND b.CNAME='JAVA' ) AS 'JAVA',  
    (SELECT (  
                    CASE  
                    WHEN SCORE - (select avg(SCORE) from stdscore where CNAME='JAVA') > 20 THEN  
                        '优秀'  
                    WHEN SCORE - (select avg(SCORE) from stdscore where CNAME='JAVA') > 10 THEN  
                        '良好'  
                    WHEN SCORE - (select avg(SCORE) from stdscore where CNAME='JAVA') >= 0 THEN  
                        '普通'  
                    ELSE  
                        '较差'  
                    END  
                ) FROM stdscore b WHERE a.sname=b.sname AND b.CNAME='mysql' ) AS 'mysql'  
    FROM stdscore a  

方法三:

  1. DROP PROCEDURE  
    IF EXISTS sp_score;  
    DELIMITER &&  
      
    CREATE PROCEDURE sp_score ()  
    BEGIN  
        #课程名称  
        DECLARE  
            cname_n VARCHAR (20) ; #所有课程数量  
            DECLARE  
                count INT ; #计数器  
                DECLARE  
                    i INT DEFAULT 0 ; #拼接SQL字符串  
                SET @s = 'SELECT sname' ;  
                SET count = (  
                    SELECT  
                        COUNT(DISTINCT cname)  
                    FROM  
                        stdscore  
                ) ;  
                WHILE i < count DO  
      
      
                SET cname_n = (  
                    SELECT  
                        cname  
                    FROM  
                        stdscore  
                    GROUP BY CNAME   
                    LIMIT i,  
                    1  
                ) ;  
                SET @s = CONCAT(  
                    @s,  
                    ', SUM(CASE cname WHEN ',  
                    '\'',  
                    cname_n,  
                    '\'',  
                    ' THEN score ELSE 0 END)',  
                    ' AS ',  
                    '\'',  
                    cname_n,  
                    '\''  
                ) ;  
                SET i = i + 1 ;  
                END  
                WHILE ;  
                SET @s = CONCAT(  
                    @s,  
                    ' FROM stdscore GROUP BY sname'  
                ) ; #用于调试  
                #SELECT @s;  
                PREPARE stmt  
                FROM  
                    @s ; EXECUTE stmt ;  
                END&&  
      
    CALL sp_score () ;  

几种方法比较分析

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值