MySQL巧用sum,case...when...优化统计查询
最近在做项目,涉及到开发统计报表相关的任务,由于数据量相对较多,之前写的查询语句查询五十万条数据大概需要十秒左右的样子,后来经过老大的指点利用sum,case...when...重写SQL性能一下子提高到一秒钟就解决了。这里为了简洁明了的阐述问题和解决的方法,我简化一下需求模型。
现在数据库有一张订单表(经过简化的中间表),表结构如下:
CREATE TABLE `statistic_order` (
`oid` bigint(20) NOT NULL,
`o_source` varchar(25) DEFAULT NULL COMMENT '来源编号',
`o_actno` varchar(30) DEFAULT NULL COMMENT '活动编号',
`o_actname` varchar(100) DEFAULT NULL COMMENT '参与活动名称',
`o_n_channel` int(2) DEFAULT NULL COMMENT '商城平台',
`o_clue` varchar(25) DEFAULT NULL COMMENT '线索分类',
`o_star_level` varchar(25) DEFAULT NULL COMMENT '订单星级',
`o_saledep` varchar(30) DEFAULT NULL COMMENT '营销部',
`o_style` varchar(30) DEFAULT NULL COMMENT '车型',
`o_status` int(2) DEFAULT NULL COMMENT '订单状态',
`syctime_day` varchar(15) DEFAULT NULL COMMENT '按天格式化日期',
PRIMARY KEY (`oid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
项目需求是这样的:
统计某段时间范围内每天的来源编号数量,其中来源编号对应数据表中的o_source字段,字段值可能为CDE,SDE,PDE,CSE,SSE。
一开始写了这样一段SQL:
select S.syctime_day,
(select count(*) from statistic_order SS where SS.syctime_day = S.syctime_day and SS.o_source = 'CDE') as 'CDE',
(select count(*) from statistic_order SS where SS.syctime_day = S.syctime_day and SS.o_source = 'CDE') as 'SDE',
(select count(*) from statistic_order SS where SS.syctime_day = S.syctime_day and SS.o_source = 'CDE') as 'PDE',
(select count(*) from statistic_order SS where SS.syctime_day = S.syctime_day and SS.o_source = 'CDE') as 'CSE',
(select count(*) from statistic_order SS where SS.syctime_day = S.syctime_day and SS.o_source = 'CDE') as 'SSE'
from statistic_order S where S.syctime_day > '2016-05-01' and S.syctime_day < '2016-08-01'
GROUP BY S.syctime_day order by S.syctime_day asc;
这种写法采用了子查询的方式,在没有加索引的情况下,55万条数据执行这句SQL,在workbench下等待了将近十分钟,最后报了一个连接中断,通过explain解释器可以看到SQL的执行计划如下:

每一个查询都进行了全表扫描,五个子查询DEPENDENT SUBQUERY说明依赖于外部查询,这种查询机制是先进行外部查询,查询出group by后的日期结果,然后子查询分别查询对应的日期中CDE,SDE等的数量,其效率可想而知。
在o_source和syctime_day上加上索引之后,效率提高了很多,大概五秒钟就查询出了结果:

查看执行计划发现扫描的行数减少了很多,不再进行全表扫描了:

这当然还不够快,如果当数据量达到百万级别的话,查询速度肯定是不能容忍的。一直在想有没有一种办法,能否直接遍历一次就查询出所有的结果,类似于遍历java中的list集合,遇到某个条件就计数一次,这样进行一次全表扫描就可以查询出结果集,结果索引,效率应该会很高。在老大的指引下,利用sum聚合函数,加上case...when...then...这种“陌生”的用法,有效的解决了这个问题。
具体SQL如下:
select S.syctime_day,
sum(case when S.o_source = 'CDE' then 1 else 0 end) as 'CDE',
sum(case when S.o_source = 'SDE' then 1 else 0 end) as 'SDE',
sum(case when S.o_source = 'PDE' then 1 else 0 end) as 'PDE',
sum(case when S.o_source = 'CSE' then 1 else 0 end) as 'CSE',
sum(case when S.o_source = 'SSE' then 1 else 0 end) as 'SSE'
from statistic_order S where S.syctime_day > '2015-05-01' and S.syctime_day < '2016-08-01'
GROUP BY S.syctime_day order by S.syctime_day asc;
关于MySQL中case...when...then的用法就不做过多的解释了,这条SQL很容易理解,先对一条一条记录进行遍历,group by对日期进行了分类,sum聚合函数对某个日期的值进行求和,重点就在于case...when...then对sum的求和巧妙的加入了条件,当o_source = 'CDE'的时候,计数为1,否则为0;当o_source='SDE'的时候......
这条语句的执行只花了一秒多,对于五十多万的数据进行这样一个维度的统计还是比较理想的。

通过执行计划发现,虽然扫描的行数变多了,但是只进行了一次全表扫描,而且是SIMPLE简单查询,所以执行效率自然就高了:

针对这个问题,如果大家有更好的方案或思路,欢迎留言
MySQL巧用sum,case...when...优化统计查询的更多相关文章
- MySQL巧建sum索引帮我们提高至少100%的效率
有两个表,表a CREATE TABLE `a` ( `id` mediumint() unsigned NOT NULL AUTO_INCREMENT, `fid` ) unsigned ', `c ...
- MySQL高级函数case的使用技巧----与sum结合实现分段统计
case 函数 CASE WHEN condition1 THEN result1 WHEN condition2 THEN result2 ... WHEN conditionN THEN resu ...
- MySQL中group by , sum , case when then 的使用
在我们使用数据库的时候,可能会遇到需要进行统计的情况. 比如需要统计一下,下表中各个年份的胜负场数. 遇到这样的情况,我们应该怎么办呢? 在mysql中我们可以使用group by sum case ...
- Mysql 使用Group 和Case When统计数据
项目是基于:thinkcmf的,新的需求是对各栏目的文章数量进行统计 SQl很简单,先根据分类ID进行分组,然后再通过CASE WHEN 再统计不同文章状态数量 ) as count , =已审核 , ...
- 【mysql】mysql统计查询count的效率优化问题
mysql统计查询count的效率优化问题 涉及到一个问题 就是 mysql的二级索引的问题,聚簇索引和非聚簇索引 引申地址:https://www.cnblogs.com/sxdcgaq8080/p ...
- mysql学习记录,CASE WHEN THEN ELSE END用法
记mysql,case when then else end用法 用法1:搜索函数 SELECT r.order_no, r.golds, r.pay_tool, , ) ) END AS price ...
- Mysql-SQL优化-统计某种类型的个数
有时我们想统计某种类型有多少个,会用这个SQL. 全表扫描之余,还要filesort.耗时1.34秒. mysql> select country,count(*) from t1 group ...
- ORACLE多条件的统计查询(case when)
前几天要做一个统计查询的功能,因为涉及多张表,多种条件的统计分析.一开始便想到了UNION和IF语句,然后写了1000多行代码,就为了查30条数据觉得不应该. 然后就开始百度,多种条件下的统计.然后有 ...
- MySQL 5.6&5.7 性能优化 TOP10(转)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/NLOneDay/article/deta ...
随机推荐
- Linux时间子系统(一) 基本概念
本文使用Q & A的方式来和大家以前探讨一下时间的基本概念 一.什么是时间? 这个问题实在是太复杂了,我都不知道这是一个物理学.宇宙学.还是热力学异或是哲学问题,我只是想从几个侧面来了解一下时 ...
- git配置gitignore
一.背景 ...
- starUML破解方法(Windows10 & MAC)
安装好,打开安装目录,依次找到[www\license\node],找到名为[LicenseManagerDomain]的js文件,打开它,在第25行位置插入以下几句代码: return { name ...
- 在ubuntu中配置深度学习python图片分类实验环境
1 安装numpy,scipy, matplotlib, sudo apt-get install python-numpy sudo apt-get install python-scipy sud ...
- shell case语法
在阅读hadoop相关的脚本文件时,遇到case语句,好久不写shell,忘了不少,复习下shell的case语句: 运行结果: ...
- SVN配置钩子
安装测试环境:109 CentOS4.6 安装: SVN1.32http://subversion.tigris.org/downloads/subversion-1.3.2.tar.gz安装:解压 ...
- jQuery.ajax() 如何设置 Headers 中的 Accept 内容
其实很简单,首先如果是常见类型,则请直接设置 dataType 属性 $.ajax({ dataType: "json", type: "get", succe ...
- Qt 自学QGraphicsItem要点 积累
1.在创建QGraphicsItem子类的时候,想要实现自己绘图,一般是重新实现boundingRect()和paint()函数,如果不重新实现shape(),基类的实现将会退而使用 bounding ...
- 【算法】转载:Iterative vs. Recursive Approaches
Iterative vs. Recursive Approaches Eyal Lantzman, 5 Nov 2007 CPOL Introduction This arti ...
- CSS3和js炫酷点击按钮3D翻转动画特效
简要教程 flipside是一款使用CSS3和js制作的炫酷点击按钮无缝过渡到确认面板的过渡动画特效.该点击按钮特效在按钮不同方向的边部点击时,产生的过渡动画特效是不一样的. 在线预览 源码下载 ...