当前位置: 代码网 > it编程>数据库>Mysql > mysql实现列转行和行转列方式

mysql实现列转行和行转列方式

2025年08月05日 Mysql 我要评论
1、行转列(将多行数据转为单行多列)1.1、使用 case when + 聚合函数select id, max(case when subject = '数学' then score e

1、行转列(将多行数据转为单行多列)

1.1、使用 case when + 聚合函数

select 
    id,
    max(case when subject = '数学' then score else null end) as '数学',
    max(case when subject = '语文' then score else null end) as '语文',
    max(case when subject = '英语' then score else null end) as '英语'
from student_scores
group by id;

1.2、使用 if + 聚合函数

select 
    id,
    max(if(subject = '数学', score, null)) as '数学',
    max(if(subject = '语文', score, null)) as '语文',
    max(if(subject = '英语', score, null)) as '英语'
from student_scores
group by id;

1.3、使用 pivot (mysql 8.0+)

select 
    id,
    json_unquote(json_extract(pivot_data, '$.数学')) as '数学',
    json_unquote(json_extract(pivot_data, '$.语文')) as '语文',
    json_unquote(json_extract(pivot_data, '$.英语')) as '英语'
from (
    select 
        id,
        json_objectagg(subject, score) as pivot_data
    from student_scores
    group by id
) as t;

1.4、dataworks使用wm_concat函数和keyvalue

  • 缺点:当字符串存在英文冒号时会导致获取的值为空;中文冒号不受影响
  • 如果存在重复的数据,将导致取数时随机取其中一个;核心原因为wm_concat函数在拼接时顺序不固定,哪怕是增加了order by也没有用
  • keyvalue从字符串中取值时,如果有重复key,从左到右取第一个key的值
select  id
        ,keyvalue(column_value,'name') as name
        ,keyvalue(column_value,'age') as age
from    (
            select  id
                    ,wm_concat(';',concat(obj_name,':',obj_value)) as column_value
            from    school
            group by id
        ) 
;
-- 如果值存在英文冒号,导致取值为空的原因,看下面两个sql例子即可理解
-- 返回null
select keyvalue('name:小红:3737;age:13','name');
-- 返回3737
select keyvalue('name:小红:3737;age:13','name:小红');

2、列转行(将多列数据转为多行)

2.1、使用 union all

select id, '数学' as subject, 数学 as score from student_scores_pivot
union all
select id, '语文' as subject, 语文 as score from student_scores_pivot
union all
select id, '英语' as subject, 英语 as score from student_scores_pivot
order by id, subject;

2.2、使用 cross join + 条件筛选

  • 优点是不用频繁读取磁盘
select 
    s.id,
    c.subject,
    case c.subject
        when '数学' then s.数学
        when '语文' then s.语文
        when '英语' then s.英语
    end as score
from student_scores_pivot s
cross join (
    select '数学' as subject union all
    select '语文' union all
    select '英语'
) c;
  • 同样的语句,使用values和row
select 
    s.id,
    c.subject,
    case c.subject
        when '数学' then s.数学
        when '语文' then s.语文
        when '英语' then s.英语
    end as score
from student_scores_pivot s
cross join (
	values
    row('数学') 
    ,row('语文')
    ,row('英语')
) c(subject);

2.3、使用 json 函数 (mysql 8.0+)

select 
    id,
    jt.subject,
    jt.score
from student_scores_pivot,
json_table(
    json_object(
        '数学', 数学,
        '语文', 语文,
        '英语', 英语
    ),
    '$.*' columns(
        subject varchar(10) path '$.key',
        score int path '$.value'
    )
) as jt;

3、动态行转列

  • 对于不确定列名的情况,可以使用存储过程动态生成sql:
delimiter //
create procedure dynamic_pivot(in table_name varchar(100), in row_id varchar(100), in pivot_col varchar(100), in value_col varchar(100))
begin
    declare done int default false;
    declare col_name varchar(100);
    declare col_list text default '';
    declare cur cursor for 
        select distinct pivot_col from table_name;
    declare continue handler for not found set done = true;
    
    open cur;
    read_loop: loop
        fetch cur into col_name;
        if done then
            leave read_loop;
        end if;
        set col_list = concat(col_list, 
            if(col_list = '', '', ', '), 
            'max(case when ', pivot_col, ' = ''', col_name, ''' then ', value_col, ' else null end) as `', col_name, '`');
    end loop;
    close cur;
    
    set @sql = concat('select ', row_id, ', ', col_list, ' from ', table_name, ' group by ', row_id, ';');
    prepare stmt from @sql;
    execute stmt;
    deallocate prepare stmt;
end //
delimiter ;

-- 调用存储过程
call dynamic_pivot('student_scores', 'id', 'subject', 'score');

4、详细测试demo

4.1、dataworks使用wm_concat函数和keyvalue实现行转列

-- 创建表
create table if not exists school (
`id` string,
`obj_name` string,
`obj_value` string
);

-- 插入测试数据
insert into school
values 
('1','name','小明'),
('1','age','12'),
('2','name','小红'),
('2','age','13')
;


-- 列转行
select  id
        ,keyvalue(column_value,'name') as name
        ,keyvalue(column_value,'age') as age
from    (
            select  id
                    ,wm_concat(';',concat(obj_name,':',obj_value)) as column_value
            from    school
            group by id
        ) 
;

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

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

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com