当前位置: 首页 > news >正文

MySQL 行转列 (Pivot) 的 N 种实现方式:静态、动态与 GROUP_CONCAT 详解

MySQL中的"行转列"是一个经典且非常实用的数据处理技巧,通常用于将数据从一种易于存储的格式转换为一种更易于阅读和报告的格式。下面我将详细解释其概念、方法和应用场景。

核心概念:什么是行转列?

-   **行数据**:通常指数据库中最自然的存储格式,每一行代表一条独立的记录。

| 学生姓名 | 科目 | 成绩 |

| :------- | :--- | :--- |

| 张三     | 语文 | 90   |

| 张三     | 数学 | 85   |

| 李四     | 语文 | 95   |

| 李四     | 数学 | 70   |

-   **列数据**(行转列后的目标):将某一列的唯一值(如`科目`)转换为新的列名,并将其对应的值(如`成绩`)填充到新列下。

| 学生姓名 | 语文 | 数学 |

| :------- | :--- | :--- |

| 张三     | 90   | 85   |

| 李四     | 95   | 70   |

这种转换也称为**数据透视(PIVOT)**。

---

### 实现行转列的几种方法

假设我们有上文的成绩表 `scores`。

方法一:使用 `CASE WHEN` + 聚合函数(最通用、最灵活)

这是最经典和兼容性最好的方法,适用于几乎所有版本的 MySQL。

**思路**:

1.  使用 `GROUP BY` 按唯一标识(如`学生姓名`)分组,确保一行一个学生。

2.  对每个需要转换的列值,使用 `CASE WHEN`(或 `IF`)语句判断并提取对应的值。

3.  使用 `MAX` 或 `MIN`、`SUM` 等聚合函数对提取出的值进行聚合(通常用于确保每个分组下只有一个非空值)。

**SQL 示例**:

```sql

SELECT

学生姓名,

MAX(CASE WHEN 科目 = '语文' THEN 成绩 ELSE NULL END) AS 语文,

MAX(CASE WHEN 科目 = '数学' THEN 成绩 ELSE NULL END) AS 数学

-- , ...可以继续添加其他科目

FROM scores

GROUP BY 学生姓名;

```

**结果**:

| 学生姓名 | 语文 | 数学 |

| :------- | :--- | :--- |

| 张三     | 90   | 85   |

| 李四     | 95   | 70   |

**为什么用 `MAX`?**

因为 `GROUP BY` 后,每个学生的每门科目理论上只有一条记录。`MAX` 的作用是取回那条唯一记录的值。如果不用聚合函数,SQL 会报错。你也可以用 `MIN` 或 `SUM`,效果相同。

方法二:使用 `IF()` + 聚合函数(MySQL 的简洁写法)

`IF(condition, value_if_true, value_if_false)` 是 `CASE WHEN` 的简化版,逻辑更清晰。

**SQL 示例**:

```sql

SELECT

学生姓名,

MAX(IF(科目 = '语文', 成绩, NULL)) AS 语文,

MAX(IF(科目 = '数学', 成绩, NULL)) AS 数学

FROM scores

GROUP BY 学生姓名;

```

效果与方法一完全相同。

方法三:使用 `GROUP_CONCAT`(另一种形式的“行转列”)

有时“转列”的目的不是生成多个新列,而是将多行数据**合并到一个单元格**中,用分隔符连接。这时 `GROUP_CONCAT` 非常有用。

**SQL 示例**:

```sql

SELECT

学生姓名,

GROUP_CONCAT(科目, ':', 成绩 SEPARATOR '; ') AS 成绩单

FROM scores

GROUP BY 学生姓名;

```

**结果**:

| 学生姓名 | 成绩单             |

| :------- | :----------------- |

| 张三     | 语文:90; 数学:85   |

| 李四     | 语文:95; 数学:70   |

---

高级应用:动态行转列

上面的方法有个巨大缺陷:**需要手动编写每个要转换的列值(如‘语文’、‘数学’)**。如果科目是不确定的(例如随时会增加‘英语’、‘物理’),上面的 SQL 就无法满足需求。

这时需要使用**存储过程(Stored Procedure)** 来**动态**生成 SQL 语句。

**思路**:

1.  查询出所有需要转换为列名的唯一值(如所有科目)。

2.  使用字符串拼接函数(如 `CONCAT`, `GROUP_CONCAT`)构造出包含所有 `CASE WHEN` 语句的 SQL 字符串。

3.  使用预处理语句(`PREPARE` & `EXECUTE`)来执行这个动态生成的 SQL 字符串。

**示例代码**:

```sql

-- 1. 定义变量存储动态生成的SQL

SET @sql = NULL;

-- 2. 查询所有不重复的科目,并拼接成 MAX(CASE WHEN...) 的格式

SELECT

GROUP_CONCAT(DISTINCT

CONCAT(

'MAX(IF(科目 = ''',

科目,

''', 成绩, NULL)) AS ',

CONCAT('`', 科目, '`') -- 用反引号包裹科目名,防止它是关键字

)

) INTO @sql

FROM scores;

-- 3. 拼接完整的SQL语句

SET @sql = CONCAT('SELECT 学生姓名, ', @sql, ' FROM scores GROUP BY 学生姓名');

-- 4. 预处理并执行动态SQL

PREPARE stmt FROM @sql;

EXECUTE stmt;

DEALLOCATE PREPARE stmt;

```

**执行结果**:无论科目如何变化,这段代码都会自动生成一个将所有科目作为列名的透视表。

| 学生姓名 | 语文 | 数学 | 英语 | (其他任何科目...) |

| :------- | :--- | :--- | :--- | :------------------ |

| 张三     | 90   | 85   | 92   | ...                 |

| 李四     | 95   | 70   | 88   | ...                 |

---

### 总结与选择

| 方法               | 适用场景                                       | 优点                         | 缺点                           |

| :----------------- | :--------------------------------------------- | :--------------------------- | :----------------------------- |

| **`CASE WHEN`/`IF`** | 要转换的列值是**固定的、已知的**(如科目固定) | 简单、直观、性能好           | 无法自动适应列值的变化         |

| **`GROUP_CONCAT`**  | 需要将多行数据合并到一个字段中显示             | 非常灵活,不会改变表结构     | 结果不是一个规整的二维表       |

| **动态SQL**        | 要转换的列值是**动态的、不确定的**(如科目可变) | 全自动,一劳永逸             | 编写复杂,需要用到存储过程和预处理 |

**核心要点**:

1.  **行转列的本质是条件判断和分组聚合**。

2.  静态转换使用 `CASE WHEN`/`IF` + `MAX` + `GROUP BY`。

3.  动态转换需要使用存储过程拼接 SQL,复杂度较高。

4.  在选择方法前,一定要明确需求中的“列”是否是固定的。

另外搭配便捷的MYSQL备份工具,可定时备份、异地备份,MYSQL导出导入。可本地连接LINUX里的MYSQL,简单便捷。可以大大地提高工作效率喔。

http://www.dtcms.com/a/365680.html

相关文章:

  • C++入门小馆:C++11第一弹
  • 面试复习题-Flutter
  • https 协议与 wss 协议有什么不同
  • 详细教程:如何利用nslookup命令查询DNS解析状态?
  • 深度学习------模型的保存和使用
  • CSS 伪类与伪元素:深度解析
  • 大疆图传技术参数对比 你了解多少?
  • 2025高教社杯数模国赛【思路预约】
  • Mysql的锁退化
  • 虚拟机+ubuntu+docker+python部署,以及中途遇到的问题和解决方案
  • 计算机科学领域-CS基础
  • 信创MySQL到达梦数据库的SQL语法转换技术解析
  • 使用Java定时爬取CSDN博客并自动邮件推送
  • CPU和GPU的区别与作用域
  • prometheus+grafana搭建
  • 虚拟机NAT模式通过宿主机(Windows)上网不稳定解决办法(无法上网)(将宿主机设置固定ip并配置dns)
  • 【面试题】OOV(未登录词)问题如何解决?
  • Unity 枪械红点瞄准器计算
  • K8S 部署 NFS Dynamic Provisioning(动态存储供应)
  • Grafana可视化平台深度解析:选型、竞品、成本与资源消耗
  • SpringCloud整合分布式事务Seata
  • C语言(长期更新)第13讲:指针详解(三)
  • 毒蛇品种检测识别数据集:12个类别,6500+图像,全yolo标注
  • 印度股票数据API对接文档
  • 硬件(一)51单片机
  • 【和春笋一起学C++】(三十九)类作用域
  • [鸿蒙心迹]带新人学鸿蒙的悲欢离合
  • “企业版维基百科”Confluence
  • Docker实战指南:从安装到架构解析
  • 【QT特性技术讲解】QPrinter、QPdf