Bohr-L Bohr-L
首页
技术
常见面试题
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

刘博

I'm a slow walker, But I never walk backwards.
首页
技术
常见面试题
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 数据处理与存储类

  • Spring 生态类

  • 缓存问题类

  • 多线程类

  • JVM 类

  • MySQL 类

    • MySQL 为什么一定要有一个主键
    • MySQL 中的 RR 隔离级别,到底有没有解决幻读问题
    • MySQL 的行级锁到底锁的是什么东西?
    • 存储 MD5 值应该用 VARCHAR 还是用 CHAR?
    • 数据库的三范式是什么?
    • 说说 InnoDB 与 MyISAM 的区别
    • drop、truncate、delete 的区别
    • 聊一聊数据库事务机制
    • 聊一聊 MySQL 中的关联查询
    • 事务隔离级别有哪些?MySQL 的默认隔离级别是什么
    • 分库分表之后,id 主键如何处理?
    • 说说在 MySQL 中一条查询 SQL 是如何执行的?
    • 讲解下 DDL、DML、DCL
    • 存储过程和触发器的作用
    • MySQL 如何行转列和列转行
      • 如何查看 SQL 的执行计划
      • union 和 unionAll 的区别
      • having 和 where 的区别
      • 常见的索引原则
      • MySQL 中的 IN 和 EXISTS 子句有什么区别?
      • MySQL 如何处理 NULL 值,对性能有什么影响?
      • 如何在 MySQL 中处理和避免全表扫描?
      • MySQL 中的表空间是什么,它的作用是什么?
      • 在 MySQL 中,如何优化 ORDER BY 查询?
    • Java 8 + 特性类

    • 其他技术类

    • 常见面试题
    • MySQL 类
    刘博
    2025-12-29
    目录

    MySQL 如何行转列和列转行

    行转列和列转行是 MySQL 中常见的数据格式转换场景,核心通过CASE WHEN、GROUP BY、CONCAT_WS、SUBSTRING_INDEX等函数实现,具体示例如下(基于以下测试表):

    # 测试表与初始数据

    -- 行转列测试表(成绩表,每行是一个学生的一门成绩)
    CREATE TABLE score (
        id INT PRIMARY KEY AUTO_INCREMENT,
        student_name VARCHAR(20), -- 学生姓名
        subject VARCHAR(20), -- 科目
        score INT -- 分数
    );
    INSERT INTO score (student_name, subject, score) VALUES
    ('张三', '语文', 90), ('张三', '数学', 85), ('张三', '英语', 95),
    ('李四', '语文', 88), ('李四', '数学', 92), ('李四', '英语', 80);
    
    -- 列转行测试表(成绩表,每列是一门科目)
    CREATE TABLE score_pivot (
        id INT PRIMARY KEY AUTO_INCREMENT,
        student_name VARCHAR(20), -- 学生姓名
        chinese INT, -- 语文
        math INT, -- 数学
        english INT -- 英语
    );
    INSERT INTO score_pivot (student_name, chinese, math, english) VALUES
    ('张三', 90, 85, 95), ('李四', 88, 92, 80);
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

    # 1. 行转列(将多行数据转换为多列)

    需求:将score表中每个学生的多门成绩(多行),转换为 “学生姓名 + 语文 + 数学 + 英语” 的一行数据。

    # 实现方式:CASE WHEN + GROUP BY
    SELECT
        student_name,
        -- 语文成绩:当科目为语文时,取分数,否则取0,分组后求和(实际只有一个非0值)
        SUM(CASE WHEN subject = '语文' THEN score ELSE 0 END) AS chinese,
        SUM(CASE WHEN subject = '数学' THEN score ELSE 0 END) AS math,
        SUM(CASE WHEN subject = '英语' THEN score ELSE 0 END) AS english
    FROM score
    GROUP BY student_name; -- 按学生分组,聚合各科成绩
    
    1
    2
    3
    4
    5
    6
    7
    8
    # 结果:
    student_name chinese math english
    张三 90 85 95
    李四 88 92 80
    # 说明:
    • 若科目可能为空,可用IFNULL替换ELSE 0(如ELSE IFNULL(score, 0));
    • 若科目较多,可结合动态 SQL 生成CASE WHEN语句(避免手动编写)。

    # 2. 列转行(将多列数据转换为多行)

    需求:将score_pivot表中每个学生的多门成绩(多列),转换为 “学生姓名 + 科目 + 分数” 的多行数据。

    # 实现方式:UNION ALL + 列拆分
    -- 语文成绩行
    SELECT student_name, '语文' AS subject, chinese AS score FROM score_pivot
    UNION ALL
    -- 数学成绩行
    SELECT student_name, '数学' AS subject, math AS score FROM score_pivot
    UNION ALL
    -- 英语成绩行
    SELECT student_name, '英语' AS subject, english AS score FROM score_pivot;
    
    1
    2
    3
    4
    5
    6
    7
    8
    # 结果:
    student_name subject score
    张三 语文 90
    李四 语文 88
    张三 数学 85
    李四 数学 92
    张三 英语 95
    李四 英语 80
    # 说明:
    • UNION ALL保留重复行(适合无重复数据的场景),UNION去重(性能略低);
    • 若列较多,可通过动态 SQL 生成多个SELECT语句,再用UNION ALL拼接。

    # 3. 扩展:动态行转列(科目不固定时)

    若score表的科目不固定(如新增 “物理”“化学”),需动态生成列名,可通过 MySQL 的存储过程实现:

    DELIMITER //
    CREATE PROCEDURE dynamic_row_to_col()
    BEGIN
        -- 1. 拼接科目列(如"MAX(CASE WHEN subject='语文' THEN score ELSE 0 END) AS 语文")
        SET @col_sql = '';
        SELECT GROUP_CONCAT(
            CONCAT("MAX(CASE WHEN subject='", subject, "' THEN score ELSE 0 END) AS ", subject)
        ) INTO @col_sql FROM (SELECT DISTINCT subject FROM score) AS t;
        
        -- 2. 拼接完整SQL
        SET @full_sql = CONCAT("SELECT student_name, ", @col_sql, " FROM score GROUP BY student_name");
        
        -- 3. 执行动态SQL
        PREPARE stmt FROM @full_sql;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
    END //
    DELIMITER ;
    
    -- 调用存储过程
    CALL dynamic_row_to_col();
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

    #


    上次更新: 12/30/2025
    存储过程和触发器的作用
    如何查看 SQL 的执行计划

    ← 存储过程和触发器的作用 如何查看 SQL 的执行计划→

    最近更新
    01
    CPU 使用率较高排查和解决
    12-29
    02
    JVM OOM 问题如何排查和解决
    12-29
    03
    接口防刷怎么实现?
    12-29
    更多文章>
    Theme by Vdoing | Copyright © 2025-2026 Bohr-L's note
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式