MySQL中的"行转列"是一个经典且非常实用的数据处理技巧,通常用于将数据从一种易于存储的格式转换为一种更易于阅读和报告的格式。下面我将详细解释其概念、方法和应用场景。
- **行数据**:通常指数据库中最自然的存储格式,每一行代表一条独立的记录。
| :------- | :--- | :--- |
- **列数据**(行转列后的目标):将某一列的唯一值(如`科目`)转换为新的列名,并将其对应的值(如`成绩`)填充到新列下。
| :------- | :--- | :--- |
方法一:使用 `CASE WHEN` + 聚合函数(最通用、最灵活)
这是最经典和兼容性最好的方法,适用于几乎所有版本的 MySQL。
1. 使用 `GROUP BY` 按唯一标识(如`学生姓名`)分组,确保一行一个学生。
2. 对每个需要转换的列值,使用 `CASE WHEN`(或 `IF`)语句判断并提取对应的值。
3. 使用 `MAX` 或 `MIN`、`SUM` 等聚合函数对提取出的值进行聚合(通常用于确保每个分组下只有一个非空值)。
MAX(CASE WHEN 科目 = '语文' THEN 成绩 ELSE NULL END) AS 语文,
MAX(CASE WHEN 科目 = '数学' THEN 成绩 ELSE NULL END) AS 数学
| :------- | :--- | :--- |
因为 `GROUP BY` 后,每个学生的每门科目理论上只有一条记录。`MAX` 的作用是取回那条唯一记录的值。如果不用聚合函数,SQL 会报错。你也可以用 `MIN` 或 `SUM`,效果相同。
方法二:使用 `IF()` + 聚合函数(MySQL 的简洁写法)
`IF(condition, value_if_true, value_if_false)` 是 `CASE WHEN` 的简化版,逻辑更清晰。
MAX(IF(科目 = '语文', 成绩, NULL)) AS 语文,
MAX(IF(科目 = '数学', 成绩, NULL)) AS 数学
方法三:使用 `GROUP_CONCAT`(另一种形式的“行转列”)
有时“转列”的目的不是生成多个新列,而是将多行数据**合并到一个单元格**中,用分隔符连接。这时 `GROUP_CONCAT` 非常有用。
GROUP_CONCAT(科目, ':', 成绩 SEPARATOR '; ') AS 成绩单
| :------- | :----------------- |
上面的方法有个巨大缺陷:**需要手动编写每个要转换的列值(如‘语文’、‘数学’)**。如果科目是不确定的(例如随时会增加‘英语’、‘物理’),上面的 SQL 就无法满足需求。
这时需要使用**存储过程(Stored Procedure)** 来**动态**生成 SQL 语句。
1. 查询出所有需要转换为列名的唯一值(如所有科目)。
2. 使用字符串拼接函数(如 `CONCAT`, `GROUP_CONCAT`)构造出包含所有 `CASE WHEN` 语句的 SQL 字符串。
3. 使用预处理语句(`PREPARE` & `EXECUTE`)来执行这个动态生成的 SQL 字符串。
-- 2. 查询所有不重复的科目,并拼接成 MAX(CASE WHEN...) 的格式
CONCAT('`', 科目, '`') -- 用反引号包裹科目名,防止它是关键字
SET @sql = CONCAT('SELECT 学生姓名, ', @sql, ' FROM scores GROUP BY 学生姓名');
**执行结果**:无论科目如何变化,这段代码都会自动生成一个将所有科目作为列名的透视表。
| 学生姓名 | 语文 | 数学 | 英语 | (其他任何科目...) |
| :------- | :--- | :--- | :--- | :------------------ |
| 张三 | 90 | 85 | 92 | ... |
| 李四 | 95 | 70 | 88 | ... |
| :----------------- | :--------------------------------------------- | :--------------------------- | :----------------------------- |
| **`CASE WHEN`/`IF`** | 要转换的列值是**固定的、已知的**(如科目固定) | 简单、直观、性能好 | 无法自动适应列值的变化 |
| **`GROUP_CONCAT`** | 需要将多行数据合并到一个字段中显示 | 非常灵活,不会改变表结构 | 结果不是一个规整的二维表 |
| **动态SQL** | 要转换的列值是**动态的、不确定的**(如科目可变) | 全自动,一劳永逸 | 编写复杂,需要用到存储过程和预处理 |
2. 静态转换使用 `CASE WHEN`/`IF` + `MAX` + `GROUP BY`。
3. 动态转换需要使用存储过程拼接 SQL,复杂度较高。
4. 在选择方法前,一定要明确需求中的“列”是否是固定的。
另外搭配便捷的MYSQL备份工具,可定时备份、异地备份,MYSQL导出导入。可本地连接LINUX里的MYSQL,简单便捷。可以大大地提高工作效率喔。