盘点编写 sql 上的那些骚操作(针对mysql而言)

前言

咋说呢,最近交接了一个XXX统计系统到我手上,点进去系统主页,看了下实现了哪些功能,页面看着很简单就那么几个统计模块,本来想着就那么几张报表的crud来着,看了下代码也还好体量也不大,于是乎美滋滋的随波逐流了,后来出现了一个bug说什么数据统计的不对,想着快速给他改掉,顺着控制层,一路摸索到mapper这,点进xml文件一看,好家伙我尼玛一个统计sql 400多行,故事的正片由此开始!

骚操作准备工作

准备如下这么一张数据表
在这里插入图片描述


SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

DROP TABLE IF EXISTS `goods`;
CREATE TABLE `goods` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '商品编号',
  `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '商品名称',
  `price` double DEFAULT NULL COMMENT '商品单价',
  `year` int DEFAULT '0' COMMENT '商品库存',
  `month` int DEFAULT NULL COMMENT '商品类型',
  `type` text CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT '类别',
  `rate` int DEFAULT '1' COMMENT '利率',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1003 DEFAULT CHARSET=utf8;

BEGIN;
INSERT INTO `goods` VALUES (1, '00002', 2, 2022, 2, '文具', 2);
INSERT INTO `goods` VALUES (2, '00002', 22, 2022, 2, '手机', 22);
INSERT INTO `goods` VALUES (3, '00003', 3, 2022, 4, '电脑', 3);
INSERT INTO `goods` VALUES (4, '00003', 33, 2022, 4, '毛巾', 33);
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;


骚操作一,深入理解 SUM()

求2022年各个月的营收,模拟环境:每种商品对应的利率可能都是不一样的,实际环境中可能 rate 字段会用一张独立的表去存储,那时的sql可能会更加复杂,此案例的列觉只为帮助大家更好的理解,sum 函数底层的执行过程

select year,month,sum(price*rate) sum from goods group by `year`,`month`;

由于写法是 sum(price*rate) 故分组后的中间状态是这样的,如下图一。根据 year、month 分组聚和成了如下这么俩条数据,那些未被聚合的字段比如:price、rate、type也是在一一对应着的,由于文具和手机对应的俩条数据被聚合成一条数据,所以我们可以使用聚合函数去操作这些字段
在这里插入图片描述
sum(price*rate) ,中间过程如下图,先是找到每一条数据的price乘对应的rate,然后才是累加操作
在这里插入图片描述
故最后的计算结果如下图
在这里插入图片描述

骚操作二, 深入理解 HAVING

追加需求:求2022年营收超过500的月份是哪个月?
骚操作一已经统计出来了各个月的营收,求超过500的营收是哪个月份只需对骚操作一的结果进行一个筛选就好了,先来列举常规几种操作

  1. 在代码中进行一个筛选
  2. 对骚操作一做一个子查询,然后使用 where 做一个数据筛选
 select * from (select year,month,sum(price*rate) sum from goods group by `year`,`month`)a where a.sum>500;
  1. 使用 HAVING,对上点 2 做一个优化,下图俩条 sql 执行结果都一样
    在这里插入图片描述

普及一下 HAVING 的用法:

  • 对已经生成的结果级进行一个筛选,筛选条件只局限结果级中存在的列,何为结果级?举个例子:就是 where、group by 都已经作用过得到的结果,此时如果还想对数据做一个筛选,就可以用 having。

如果有的小伙伴还对 having 有点陌生,可以重点分析下下图一的第二条sql,对应的结果也在下图一
结果

骚操作三,深入理解 CONCAT()

求2022年2月到2022年5月的所有数据,但是年、月是分俩个字段存储的

select *,CONCAT(year,month) date from goods where CONCAT(year,month)>='20222' and CONCAT(year,month)<='20225';

打破思维误区!直接在查询条件使用 CONCAT 函数拼接字段进行查询,就可以了,查询 sql 以及返回结果如下图一

在这里插入图片描述

骚操作四 ,深入理解sql列

在查出的结果级上面手动的添加一列,并赋予默认值,基本没咋用到,图一乐就行

select name,'自定义填充数据' customDate from goods GROUP BY name;

在这里插入图片描述

常规操作,IF、IFNULL、ROUND

这俩个函数也非常实用,当某些字段为null的时候,可以自定义一些默认值。ROUND(a,b),为 a 保留 b 位小数。

select id,if(id=1,'-',id),IFNULL(id,0),ROUND(id/100,4) from goods;

在这里插入图片描述

关于mysql中的异或条件查询,避坑!!!!

最近再改这么一个sql:查询企业前十排名,力度精确到省、市、区县。由于这个接口很多地方都用到了,考虑到接口的兼容性,入参、出参的格式也不好做改动,当时的入参是这样的,areaCode 有可能是区县代码、也可能是省市代码。因此我们在做逻辑查询的时候,查询条件必定少不了 “或” 查询。

{
    "year": "2022",
    "month": "7",
    "areaCode": "330110" //地区代码
}

当时就是很简单的在 where 后面加了个 or ,但是实际跑下来感觉到查出的数据有点不对劲,sql本意是想:当 area_code 传的是省市代码,查出 2022 年月份小于 7 并且 trade_type 为出口的省市数据,当 area_code 传的是区县代码,查出 2022 年月份小于 7 并且 trade_type 为出口的区县数据,但是细看如下 sql ,我们会发现当 area_code 传的是区县代码的时候,代码片段2在代码片段1中直接失效了,最终的查询条件只有这一个: district_code = 330110 生效,于是修改sql成代码片段3那样就可以达到sql本意了。在java编码中条件短路的情况很常见,到了sql中同样也需要我们注意一下!

代码片段1

        WHERE
		`year` = 2022 
		AND `month` <= CAST( 7 AS UNSIGNED ) 
		AND trade_type = 'ck' 
		AND area_code = 330110 
		OR district_code = 330110 

代码片段2

`year` = 2022 
		AND `month` <= CAST( 7 AS UNSIGNED ) 
		AND trade_type = 'ck' 
		AND area_code = 330110 

代码片段3

        WHERE
		`year` = 2022 
		AND `month` <= CAST( 7 AS UNSIGNED ) 
		AND trade_type = 'ck' 
		AND area_code = 330110 
		OR `year` = 2022 
		AND `month` <= CAST( 7 AS UNSIGNED ) 
		AND trade_type = 'ck' 
		AND district_code = 330110 

后面还有别的骚操作,本文会陆续更新~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小咸鱼的技术窝

你的鼓励将是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值