MySQL系列之行转列,列转行
之前业务出现了需要行转列的场景,记录一下
SQL中AVG、COUNT、SUM、MAX等聚合函数对NULL值的处理
Mysql Max、 Where和 Group By 三个关键字同时使用 执行顺序
MySql 行转列的玩法 ,实战案例教学(MAX函数的坑简析)(本文主要参考该博主的文章)
行转列方式(个人比较推荐第三种)
使用该文章作者的数据库设计https://blog.csdn.net/qq_35387940/article/details/128264385
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',
`item_value2` int(12) NULL DEFAULT NULL COMMENT '参数项值2' 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`,`item_value`, `date`) VALUES (1, '深圳', '温度', '12',2, '2022-11-01');
INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`,`item_value`, `date`) VALUES (2, '深圳', '湿度', NULL,2, '2022-11-01');
INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`,`item_value`, `date`) VALUES (3, '深圳', '光照', '1300',2, '2022-11-01');
INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`,`item_value`, `date`) VALUES (4, '成都', '温度', '45',2, '2022-11-01');
INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`,`item_value`, `date`) VALUES (5, '成都', '湿度', '32',2, '2022-11-01');
INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`,`item_value`, `date`) VALUES (6, '成都', '光照', '1300',2, '2022-11-01');
INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`,`item_value`, `date`) VALUES (7, '成都', '湿度', '32',2, '2022-11-02');
INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`,`item_value`, `date`) VALUES (8, '成都', '温度', '26',2, '2022-11-02');
INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`,`item_value`, `date`) VALUES (9, '成都', '光照', '230',2, '2022-11-02');
INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`,`item_value`, `date`) VALUES (10, '深圳', '温度', '26',2, '2022-11-02');
INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`,`item_value`, `date`) VALUES (11, '深圳', '湿度', '34',2, '2022-11-02');
INSERT INTO `env_climate` (`id`, `area`, `item_name`, `item_value`,`item_value`, `date`) VALUES (12, '深圳', '光照', '5000',2, '2022-11-02');
groupby和min()/max()(不太推荐)
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
在以下情况会异常
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
可见所有湿度均为0,这是因为‘无’这个字的字典序比任意的一项item_value都要大,而且这个字典序和建立数据库或建表设置的字符编码有关
比如11-01的深圳这天,
MAX(CASE WHEN item_name=‘湿度’ THEN item_value ELSE '无’END) AS “湿度”
会在三个数据内对比,12,null,1300,,经过case when处理后结果是,无,null,无,这三个中字典序最大的是无,所以显示的无,其他同理
所以这个需要你了解else设置的默认值和item_value的字典序哪个大,如果是数字类型,在编码集中是连续的,需要你确实好要设置的默认值的编码序列大于任何一个数字,或者是使用min()设置比任何数字编码序列小的默认值,这是很繁琐的,所以不推荐
groupby和sum()(不太推荐)
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
不太推荐这种,因为0一般是有实际意义的,就比如温度有时候可以为0摄氏度,这和没有获取到数据是两回事
还有就算对无法转成数字类型的字符串类型,会把结果置为0,就算你用CASE WHEN item_name=‘湿度’ THEN item_value **ELSE ‘–’**END也没有任何作用
因为sum的固定返回值是double类型,但max可以返回定义的else值,因为他除了可以比较数值类型的大小,还能比较字符类型的字典序,但他有别的问题,后面会谈到这个问题
groupby和group_concat()(推荐)
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;
这种暂时未发现问题
列转行方式
主要是使用union
挖坑后补
MAX()及MIN()误区
groupby及聚合函数误区
聚合函数对null值的处理
AVG()忽略null,而不是作为0参与计算
COUNT(*) 计数不忽略null
COUNT(字段名)忽略null
MAX(),MIN()都忽略null
SUM()忽略null(可以对单个列求和,也可以对多个列运算后求和,且当对多个列运算求和时,如果运算的列中任意一列的值为NULL,则忽略这行的记录)
SELECT DATE AS '日期', AREA,
sum(item_value+item_value2)
FROM env_climate
GROUP BY DATE,AREA;
可见结果是1316,未统计11-01当天湿度的item_value2值
但是单列sum()无任何影响,因为就算按null为0,对结果也没有任何影响
SELECT DATE AS '日期', AREA,
sum(item_value)
FROM env_climate
GROUP BY DATE,AREA;
grouby的注意事项
1、分组列中若有NULL,这也将作为一组,且NULL值排在最前面
2、除汇总函数计算语句外,SELECT中的选择列必须出现在GROUP BY 中(这个可以通过更改sqlmode的ONLY_FULL_GROUP_BY绕过这一限制)
3、GROUP BY 可以包含任意数目的列,可以嵌套