MySQL行列转换及多选字典

本文介绍如何在MySQL中将包含逗号分隔值的字段进行列转行操作,并结合字典表实现科目名称的匹配,进一步完成成绩数据的整合。通过不同SQL语句的对比,展示如何高效地进行数据转换。

数据准备

建表


-- 学生表
-- DROP TABLE tb_stu;
CREATE TABLE tb_stu (
  id int(11) NOT NULL AUTO_INCREMENT,
  userid varchar(20) NOT NULL COMMENT '用户id',
  name varchar(20) DEFAULT NULL COMMENT '姓名',
  subjects varchar(64) DEFAULT NULL COMMENT '科目',
  PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- 成绩表
-- DROP TABLE tb_score;
CREATE TABLE tb_score (
  id int(11) NOT NULL AUTO_INCREMENT,
  userid varchar(20) NOT NULL COMMENT '用户id',
  subject varchar(20) DEFAULT NULL COMMENT '科目',
  score double DEFAULT NULL COMMENT '成绩',
  PRIMARY KEY (id)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- 字典表
-- DROP TABLE tb_dic;
CREATE TABLE tb_dic (
  id int(11) NOT NULL AUTO_INCREMENT,
  code varchar(20) NOT NULL COMMENT '字典-代码',
  text varchar(20) DEFAULT NULL COMMENT '字典-文字',
  seq int(2) DEFAULT NULL COMMENT '排序',
  PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;


写入数据

-- 学生表
INSERT INTO tb_stu(userid,name,subjects) VALUES ('001','小明','YW,SX,YY');
INSERT INTO tb_stu(userid,name,subjects) VALUES ('002','小锋','YW,SX,YY');
INSERT INTO tb_stu(userid,name,subjects) VALUES ('002','小军','YW,SX,YY,ZZ');

-- 成绩表
INSERT INTO tb_score(userid,subject,score) VALUES ('001','YW',90);
INSERT INTO tb_score(userid,subject,score) VALUES ('001','SX',92);
INSERT INTO tb_score(userid,subject,score) VALUES ('001','YY',80);
INSERT INTO tb_score(userid,subject,score) VALUES ('002','YW',88);
INSERT INTO tb_score(userid,subject,score) VALUES ('002','SX',90);
INSERT INTO tb_score(userid,subject,score) VALUES ('002','YY',75.5);
INSERT INTO tb_score(userid,subject,score) VALUES ('003','YW',70);
INSERT INTO tb_score(userid,subject,score) VALUES ('003','SX',85);
INSERT INTO tb_score(userid,subject,score) VALUES ('003','YY',90);
INSERT INTO tb_score(userid,subject,score) VALUES ('003','ZZ',82);


-- 字典表
INSERT INTO tb_dic(code,text,seq) VALUES ('YW','语文',1);
INSERT INTO tb_dic(code,text,seq) VALUES ('SX','数学',2);
INSERT INTO tb_dic(code,text,seq) VALUES ('YY','英语',3);
INSERT INTO tb_dic(code,text,seq) VALUES ('ZZ','政治',4);

查询验证

第1步:逗号分割 - 列转行

SQL

SELECT
    stu.name,
    stu.subjects,
    SUBSTRING_INDEX(SUBSTRING_INDEX(stu.subjects, ',', mht.help_topic_id + 1),
            ',',
            - 1) subj
FROM
    test.tb_stu stu
        JOIN
    mysql.help_topic mht ON mht.help_topic_id < (LENGTH(stu.subjects) - LENGTH(REPLACE(stu.subjects, ',', '')) + 1)
ORDER BY stu.userid;

RESULT

useridnamesubjectssubj
001小明YW,SX,YYYW
001小明YW,SX,YYSX
001小明YW,SX,YYYY
002小锋YW,SX,YYYW
002小锋YW,SX,YYSX
002小锋YW,SX,YYYY
003小军YW,SX,YY,ZZYW
003小军YW,SX,YY,ZZSX
003小军YW,SX,YY,ZZYY
003小军YW,SX,YY,ZZZZ

第2步:逗号分割 - 列转行 - 字典匹配

SQL

SELECT
    tst.userid, tst.name, tst.subjects, tst.subj, dic.text
FROM
    (SELECT
        stu.userid,
            stu.name,
            stu.subjects,
            SUBSTRING_INDEX(SUBSTRING_INDEX(stu.subjects, ',', mht.help_topic_id + 1), ',', - 1) subj
    FROM
        tb_stu stu
    JOIN mysql.help_topic mht ON mht.help_topic_id < (LENGTH(stu.subjects) - LENGTH(REPLACE(stu.subjects, ',', '')) + 1)) tst
        LEFT JOIN
    tb_dic dic ON tst.subj = dic.code
ORDER BY tst.userid;

RESULT

useridnamesubjectssubjtext
001小明YW,SX,YYYW语文
001小明YW,SX,YYSX数学
001小明YW,SX,YYYY英语
002小锋YW,SX,YYYW语文
002小锋YW,SX,YYSX数学
002小锋YW,SX,YYYY英语
003小军YW,SX,YY,ZZYW语文
003小军YW,SX,YY,ZZSX数学
003小军YW,SX,YY,ZZYY英语
003小军YW,SX,YY,ZZZZ政治

第3步:逗号分割 - 列转行 - 字典匹配 - 分组合并(行转列)

SQL

  • V1
SELECT
    tst.userid,
    tst.name,
    tst.subjects,
    -- tst.subj,
    -- dic.text,
    GROUP_CONCAT(dic.text) subjects_cn
FROM
    (SELECT
        stu.userid,
            stu.name,
            stu.subjects,
            SUBSTRING_INDEX(SUBSTRING_INDEX(stu.subjects, ',', mht.help_topic_id + 1), ',', - 1) subj
    FROM
        tb_stu stu
    JOIN mysql.help_topic mht ON mht.help_topic_id < (LENGTH(stu.subjects) - LENGTH(REPLACE(stu.subjects, ',', '')) + 1)) tst
        LEFT JOIN
    tb_dic dic ON tst.subj = dic.code
GROUP BY tst.userid , tst.name , tst.subjects
ORDER BY tst.userid;
  • V2
SELECT
    stu.userid,
    stu.name,
    stu.subjects,
    (SELECT
            GROUP_CONCAT(dic.text) subjects_cn
        FROM
            tb_dic dic
        WHERE
            1 = 1
                AND dic.code IN (SELECT DISTINCT
                    (REGEXP_SUBSTR(T.subjects, '[^,]+', 1, mht.help_topic_id))
                FROM
                    (SELECT stu.subjects) T
                        JOIN
                    mysql.help_topic mht ON mht.help_topic_id <= (CHAR_LENGTH(T.subjects) - CHAR_LENGTH(REPLACE(T.subjects, ',', '')) + 1))) subjects_cn
FROM
    tb_stu stu
ORDER BY stu.userid
  • V3 [2021/11/9 13:56]
SELECT 
    stu.userid,
    stu.name,
    stu.subjects,
    (SELECT 
            GROUP_CONCAT(dic.text)
        FROM
            tb_dic dic
        WHERE
            FIND_IN_SET(dic.code, stu.subjects)) subjects_cn
FROM
    tb_stu stu
ORDER BY stu.userid

RESULT

useridnamesubjectssubjects_cn
001小明YW,SX,YY语文,数学,英语
002小锋YW,SX,YY语文,数学,英语
003小军YW,SX,YY,ZZ语文,数学,英语,政治

第4步:逗号分割 - 列转行 - 字典匹配 - 分组合并(行转列) - 其他表

SQL

  • V1
SELECT
    tst.userid,
    tst.name,
    tst.subjects,
    -- tst.subj,
    -- dic.text,
    GROUP_CONCAT(dic.text) subjects_cn,
    GROUP_CONCAT(dic.text, ':', sco.score) scores_cn
FROM
    (SELECT
        stu.userid,
            stu.name,
            stu.subjects,
            SUBSTRING_INDEX(SUBSTRING_INDEX(stu.subjects, ',', mht.help_topic_id + 1), ',', - 1) subj
    FROM
        tb_stu stu
    JOIN mysql.help_topic mht ON mht.help_topic_id < (LENGTH(stu.subjects) - LENGTH(REPLACE(stu.subjects, ',', '')) + 1)) tst
        LEFT JOIN
    tb_dic dic ON tst.subj = dic.code
        LEFT JOIN
    tb_score sco ON tst.userid = sco.userid
        AND tst.subj = sco.subject
GROUP BY tst.userid , tst.name , tst.subjects
ORDER BY tst.userid;
  • V2 [2021/11/9 14:03]
SELECT 
    stu.userid,
    stu.name,
    stu.subjects,
    (SELECT 
            GROUP_CONCAT(dic.text, ':', sco.score)
        FROM
            tb_dic dic
        WHERE
            FIND_IN_SET(dic.code, stu.subjects)) scores_cn
FROM
    tb_stu stu
        LEFT JOIN
    tb_score sco ON stu.userid = sco.userid
ORDER BY stu.userid

RESULT

useridnamesubjectssubjects_cnscores_cn
001小明YW,SX,YY语文,数学,英语语文:90,数学:92,英语:80
002小锋YW,SX,YY语文,数学,英语语文:88,数学:90,英语:75.5
003小军YW,SX,YY,ZZ语文,数学,英语,政治语文:70,数学:85,英语:90,政治:82

其他方式

SUBSTRING_INDEX

  • MySQL8下
with numbers as (
  select 1 as n
  union select 2 as n
  union select 3 as n
  union select 4 as n
  union select 5 as n
  union select 6 as n
  union select 7 as n
  union select 8 as n
  union select 9 as n
)
SELECT
    stu.userid,
    SUBSTRING_INDEX(SUBSTRING_INDEX(stu.subjects, ',', n),
            ',',
            - 1) AS sub_value
FROM
    tb_stu stu
        JOIN
    numbers ON CHAR_LENGTH(stu.subjects) - CHAR_LENGTH(REPLACE(stu.subjects, ',', '')) >= n - 1
    order by stu.userid

REGEXP_SUBSTR

SELECT
    stu.userid,
    REGEXP_SUBSTR(stu.subjects, '[^,]+', 1, mht.help_topic_id) AS sub_value
FROM
    tb_stu stu
        JOIN
    mysql.help_topic mht ON mht.help_topic_id <= (CHAR_LENGTH(stu.subjects) - CHAR_LENGTH(REPLACE(stu.subjects, ',', '')) + 1)
ORDER BY stu.userid

参考

Links

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值