MySQL系列之行转列,列转行

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');
 

MySQL系列之行转列,列转行_第1张图片

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

MySQL系列之行转列,列转行_第2张图片

在以下情况会异常

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()设置比任何数字编码序列小的默认值,这是很繁琐的,所以不推荐

MySQL系列之行转列,列转行_第3张图片

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

MySQL系列之行转列,列转行_第4张图片

不太推荐这种,因为0一般是有实际意义的,就比如温度有时候可以为0摄氏度,这和没有获取到数据是两回事

还有就算对无法转成数字类型的字符串类型,会把结果置为0,就算你用CASE WHEN item_name=‘湿度’ THEN item_value **ELSE ‘–’**END也没有任何作用

因为sum的固定返回值是double类型,但max可以返回定义的else值,因为他除了可以比较数值类型的大小,还能比较字符类型的字典序,但他有别的问题,后面会谈到这个问题

MySQL系列之行转列,列转行_第5张图片

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;

MySQL系列之行转列,列转行_第6张图片

这种暂时未发现问题

列转行方式

主要是使用union

挖坑后补

MAX()及MIN()误区

groupby及聚合函数误区

聚合函数对null值的处理

AVG()忽略null,而不是作为0参与计算

COUNT(*) 计数不忽略null

COUNT(字段名)忽略null

MAX(),MIN()都忽略null

SUM()忽略null(可以对单个列求和,也可以对多个列运算后求和,且当对多个列运算求和时,如果运算的列中任意一列的值为NULL,则忽略这行的记录)

image-20230716195535765

SELECT DATE AS '日期', AREA,
       sum(item_value+item_value2)
FROM env_climate
GROUP BY DATE,AREA;

MySQL系列之行转列,列转行_第7张图片

可见结果是1316,未统计11-01当天湿度的item_value2值

但是单列sum()无任何影响,因为就算按null为0,对结果也没有任何影响

SELECT DATE AS '日期', AREA,
       sum(item_value)
FROM env_climate
GROUP BY DATE,AREA;

MySQL系列之行转列,列转行_第8张图片

grouby的注意事项

1、分组列中若有NULL,这也将作为一组,且NULL值排在最前面

2、除汇总函数计算语句外,SELECT中的选择列必须出现在GROUP BY 中(这个可以通过更改sqlmode的ONLY_FULL_GROUP_BY绕过这一限制)

3、GROUP BY 可以包含任意数目的列,可以嵌套

你可能感兴趣的:(MySQL优化,mysql,数据库)