开发中遇到了行列转化的问题,在这里写出自己的实现方法,如果大家还有什么更好的方法,希望可以共同学习进步😁。
行转列,基础数据准备(表:student_row_to_column)
DROP TABLE IF EXISTS `student_row_to_column`;
CREATE TABLE `student_row_to_column` (
`id` int NOT NULL,
`t_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
`course` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
`score` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `student_row_to_column` VALUES (1, '张三', '数学', 80);
INSERT INTO `student_row_to_column` VALUES (2, '张三', '语文', 79);
INSERT INTO `student_row_to_column` VALUES (3, '张三', '英语', 60);
INSERT INTO `student_row_to_column` VALUES (4, '李四', '数学', 88);
INSERT INTO `student_row_to_column` VALUES (5, '李四', '语文', 77);
INSERT INTO `student_row_to_column` VALUES (6, '李四', '英语', 66);
最终实现效果:
方法一:case/if
- 要想实现行转列,需要先把每一行的数据,都添加为结果数据,这里就用到了 case / if (两种语句都可以)
-- 分类(case)
SELECT t_name,
CASE course WHEN '数学' THEN score ELSE NULL END as match_score,
CASE course WHEN '语文' THEN score ELSE NULL END as chinese_score,
CASE course WHEN '英语' THEN score ELSE NULL END as english_score
FROM student_row_to_column;
-- 分类(if)
SELECT t_name,
MIN(IF(course='数学',score,NULL)) as match_score,
MIN(IF(course='语文',score,NULL)) as chinese_score,
MIN(IF(course='英语',score,NULL)) as english_score
FROM student_row_to_column;
运行效果如下:
- 这时,问题就变得的简单了,我们只需要给数据进行分组就行,这里要注意,在分组的时候,要用到聚合函数
SELECT t_name,
MAX(CASE course WHEN '数学' THEN score ELSE NULL END) as match_score,
MAX(CASE course WHEN '语文' THEN score ELSE NULL END) as chinese_score,
MAX(CASE course WHEN '英语' THEN score ELSE NULL END) as english_score
FROM student_row_to_column
GROUP BY t_name;
效果如下:
弊端:
当科目变化的时候(增加/减少),我们需要修改SQL语句。
方法二:group_concat
- 利用group_concat来展示, 这样展示的效果不够直观
SELECT
t_name,
GROUP_CONCAT( course SEPARATOR ',' ) AS course,
GROUP_CONCAT( score SEPARATOR ',' ) AS score
FROM
student_row_to_column
GROUP BY
t_name;
效果:
- 放到一起,这样看起来更方便
效果:SELECT t_name, GROUP_CONCAT( course, ':', score SEPARATOR ',' ) AS 'course:score' FROM student_row_to_column GROUP BY t_name;
弊端:
虽然不会因为科目的多少而修改SQL,但是它展现的数据不能分开。
列转行,基础数据准备(表:student_column_to_row)
DROP TABLE IF EXISTS `student_column_to_row`;
CREATE TABLE `student_column_to_row` (
`id` int NOT NULL,
`t_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
`match_score` int DEFAULT NULL,
`chinese_score` int DEFAULT NULL,
`english_score` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `student_column_to_row` VALUES (1, '张三', 80, 79, 60);
INSERT INTO `student_column_to_row` VALUES (2, '李四', 88, 77, 66);
最终实现效果:
方法一: union all
-
我们实现列转行,就是把多列数据拆分为多先,这里用到了 union all
SELECT t_name, 'match_score' as course, match_score as score FROM student_column_to_row UNION ALL SELECT t_name, 'chinese_score' as course, chinese_score as score FROM student_column_to_row UNION ALL SELECT t_name, 'english_score' as course, english_score as score FROM student_column_to_row效果:
-
之后,只需要整理一下数据,把姓名相同到的放到一起即可。这里需要在嵌套一层。
SELECT * FROM ( SELECT t_name, 'match_score' as course, match_score as score FROM student_column_to_row UNION ALL SELECT t_name, 'chinese_score' as course, chinese_score as score FROM student_column_to_row UNION ALL SELECT t_name, 'english_score' as course, english_score as score FROM student_column_to_row) t ORDER BY t.t_name ASC, t.course ASC效果:
总结:
- 行转列,用case/if
- 列转先,用union all
大家还有什么方法,说出来一起进步一下吧😁。