既然有人问到mysql的行专列,那么就一块来玩一玩。
先准备个样例数据:
环境气候表 (假装是一张用于接收某个检测数据每天上报过来的环境数据)
建表SQL:
CREATE TABLE `env_climate` ( `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `area` VARCHAR(50) NULL DEFAULT NULL COMMENT '地区' COLLATE 'utf8_general_ci', `item_name` VARCHAR(50) NULL DEFAULT NULL COMMENT '参数项名' COLLATE 'utf8_general_ci', `item_value` VARCHAR(50) NULL DEFAULT NULL COMMENT '参数项值' COLLATE 'utf8_general_ci', `date` VARCHAR(50) NULL DEFAULT NULL COMMENT '日期' COLLATE 'utf8_general_ci', PRIMARY KEY (`id`) USING BTREE ) COMMENT='环境气候表' COLLATE='utf8_general_ci' ENGINE=InnoDB AUTO_INCREMENT=1 ;
然后填充一些数据:
INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`, `date`) VALUES (1, '深圳', '温度', '12', '2022-11-01'); INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`, `date`) VALUES (2, '深圳', '湿度', NULL, '2022-11-01'); INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`, `date`) VALUES (3, '深圳', '光照', '1300', '2022-11-01'); INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`, `date`) VALUES (4, '成都', '温度', '45', '2022-11-01'); INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`, `date`) VALUES (5, '成都', '湿度', '32', '2022-11-01'); INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`, `date`) VALUES (6, '成都', '光照', '1300', '2022-11-01'); INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`, `date`) VALUES (7, '成都', '湿度', '32', '2022-11-02'); INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`, `date`) VALUES (8, '成都', '温度', '26', '2022-11-02'); INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`, `date`) VALUES (9, '成都', '光照', '230', '2022-11-02'); INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`, `date`) VALUES (10, '深圳', '温度', '26', '2022-11-02'); INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`, `date`) VALUES (11, '深圳', '湿度', '34', '2022-11-02'); INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`, `date`) VALUES (12, '深圳', '光照', '5000', '2022-11-02');
看一下示例数据情况:
可以看到,本来温度、地区 、数值 都是一行行的数据 ,我们转列之后,以日期&地区的维度,直观看到了 属于这个地区的 温度、湿度、光照 等数据。
怎么玩的呢?
接下来看SQL,介绍 3种小玩法。
第一种 用MAX + GROUP 实现 (有坑,文末会讲)
SELECT DATE AS '日期', AREA, MAX(CASE WHEN item_name='温度' THEN item_value ELSE '--'END) AS "温度", MAX(CASE WHEN item_name='湿度' THEN item_value ELSE '--'END) AS "湿度", MAX(CASE WHEN item_name='光照' THEN item_value ELSE '--'END) AS "光照" FROM env_climate GROUP BY AREA, DATE
效果:
第二种 使用SUM +GROUP BY 实现
SELECT DATE AS '日期', AREA AS '地区', SUM(CASE WHEN item_name='温度' THEN item_value ELSE '--'END) AS "温度", SUM(CASE WHEN item_name='湿度' THEN item_value ELSE '--'END) AS "湿度", SUM(CASE WHEN item_name='光照' THEN item_value ELSE '--'END) AS "光照" FROM env_climate GROUP BY AREA, DATE
效果:
ps:因为SUM是累计,所以只针对数字做了计算,可以看到我们value值尽管是字符串类型,mysql在做计算还是很友好过滤掉了非数字的值,所以计算完后返回的值,变成蓝色,代表是数字
第三种 使用 GROUP_CONCAT +GROUP BY 实现
SELECT DATE AS '日期', AREA, group_concat(CASE WHEN item_name = '温度' THEN item_value END SEPARATOR '') '温度', group_concat(CASE WHEN item_name = '湿度' THEN item_value END SEPARATOR '') '湿度', group_concat(CASE WHEN item_name = '光照' THEN item_value END SEPARATOR '') '光照' FROM env_climate GROUP BY DATE,AREA;
效果:
!!!!!注意点
我们把NULL 情况 赋值 ‘ 无’ Max 的劣质行为就暴露了:
MAX(CASE WHEN item_name='湿度' THEN item_value ELSE '无'END) AS "湿度"
为什么会这样呢?
其实如果有经验的SQL人,都知道MAX 的玩法是有坑的,它只保证了 被MAX 的这个值的最大值。
然后如果你是字符串去MAX, 那么 mysql就会去比较这个值的 从左到右的一个个字符去做比较。
这么说可能还是有人不清楚。
立马做几个小例子,来讲这些坑暴露出来给大知道。
坑一 针对 varchar 做 max 、order排序
搞个新表,age字段故意搞成 字符串 varchar 类型
好玩的来咯:
可以看到针对varchar 类型 ,去做 max ,显然是跟我们想象不一样的。
其实这仅仅是一个针对varchar 使用max的 一个坑。
那么其实使用max 和 group by ,还有坑没? 有。
坑二 ,max和group 多字段 乱数据
我们 想根据SID 分组 找出 score最大的 行值 ,然后展示当前行的 sid 、score、 cid :
错误踩坑:
SELECT sid,cid,MAX(score) as score
FROM stu_score
GROUP BY sid;
因为用max 配和 group的时候, mysql是个脑子转的很快但是想东西很简单的小孩,他只管把max的这个字段超出最大值,然后其他字段都是 图快,先拿到符合条件 sid = 1001的那行,就把相关其他字段都直接赋值了。 所以 第一个sid=1001 的 那行,cid是101, 赋值。
所以出来了
不好意思,这篇是讲行转列,不小心跑题了。 那就先到这里吧。