MySQL本身没有内置的pivot和unpivot函数,但可以通过其他函数和语法组合实现行列转换的需求,常见的实现方式包括case when条件判断、group_concat聚合拼接、union all组合等,不同场景可以选择不同的方案。

一、行转列的实现方法
1. 使用case when配合聚合函数
这是最常用的行转列方式,适合需要将多行相同维度的数据合并成一行不同列的场景。假设我们有学生成绩表score,结构如下:
-- 创建成绩表
CREATE TABLE score (
student_name VARCHAR(20),
subject VARCHAR(20),
score INT
);
-- 插入测试数据
INSERT INTO score VALUES
('张三', '语文', 90),
('张三', '数学', 85),
('张三', '英语', 88),
('李四', '语文', 92),
('李四', '数学', 89),
('李四', '英语', 91);
如果需要将每个学生的不同科目成绩转成一行,语文、数学、英语作为单独的列,可以使用以下SQL:
SELECT
student_name,
MAX(CASE WHEN subject = '语文' THEN score ELSE 0 END) AS 语文,
MAX(CASE WHEN subject = '数学' THEN score ELSE 0 END) AS 数学,
MAX(CASE WHEN subject = '英语' THEN score ELSE 0 END) AS 英语
FROM score
GROUP BY student_name;
执行后结果会将每个学生的三科成绩合并到一行,分别对应三个列。这里的case when用于判断科目类型,聚合函数MAX用于取对应科目的分数,GROUP BY用于按学生分组。
2. 使用group_concat拼接行数据
如果需要将多行数据拼接成一个字符串放在单列中,可以使用group_concat函数,适合不需要拆分列只需要合并内容的场景。
SELECT
student_name,
GROUP_CONCAT(subject, ':', score ORDER BY subject SEPARATOR '; ') AS 成绩详情
FROM score
GROUP BY student_name;
执行后每个学生的所有科目成绩会以“科目:分数”的格式拼接成一个字符串,用分号分隔,方便查看所有成绩信息。
二、列转行的实现方法
1. 使用union all拆分列数据
列转行需要将一行中的多个列拆分成多行,常用union all组合多个查询结果实现。假设我们有学生成绩宽表score_wide,结构如下:
-- 创建宽表
CREATE TABLE score_wide (
student_name VARCHAR(20),
chinese INT,
math INT,
english INT
);
-- 插入测试数据
INSERT INTO score_wide VALUES
('张三', 90, 85, 88),
('李四', 92, 89, 91);
如果需要将宽表转成和之前score表一样的长表结构,可以使用以下SQL:
SELECT student_name, '语文' AS subject, chinese AS score FROM score_wide UNION ALL SELECT student_name, '数学' AS subject, math AS score FROM score_wide UNION ALL SELECT student_name, '英语' AS subject, english AS score FROM score_wide ORDER BY student_name, subject;
每个union all分支负责将一个列转成一行,最后合并所有结果,得到标准的长表结构。
三、不同场景的选择建议
- 如果需要将行数据转成多个独立列,优先选择case when配合聚合函数的方式,逻辑清晰且性能稳定。
- 如果只是需要合并行数据展示,不需要拆分列,使用group_concat更高效。
- 列转行场景优先使用union all,兼容性最好,所有MySQL版本都支持。
- 如果数据量较大,行转列时需要注意case when的分支不要过多,避免SQL过于冗长,可以适当使用动态SQL生成对应的语句。
四、注意事项
使用case when做行转列时,聚合函数的选择要根据数据特点决定,如果分数不可能为0,用MAX或MIN都可以,如果存在0值,可以改用SUM配合判断,避免把实际的0分和默认填充的0混淆。
使用union all做列转行时,每个分支的字段数量和类型必须一致,否则会报错。如果列的数据类型不同,需要做对应的类型转换,保证所有分支的字段类型匹配。