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简单查询,所以执行效率自然就高了:

时间: 2024-08-10 17:28:49

MySQL巧用sum,case...when...优化统计查询的相关文章

MySQL巧建sum索引帮我们提高至少100%的效率

有两个表,表a CREATE TABLE `a` ( `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, `fid` smallint(6) unsigned NOT NULL DEFAULT '0', `cnt` smallint(6) unsigned NOT NULL DEFAULT '0', ... ... ... PRIMARY KEY (`id`), KEY `idx_fid` (`fid`), ) ENGINE=MyISAM DE

Mysql——SQL优化-统计某种类型的个数

有时我们想统计某种类型有多少个,会用这个SQL.全表扫描之余,还要filesort,耗时1.34秒. mysql> select country,count(*) from t1 group by country; +---------+----------+ | country | count(*) | +---------+----------+ | NULL | 32 | | africa | 524288 | | america | 524288 | | china | 524288 |

Mysql 使用Group 和Case When统计数据

项目是基于:thinkcmf的,新的需求是对各栏目的文章数量进行统计 SQl很简单,先根据分类ID进行分组,然后再通过CASE WHEN 再统计不同文章状态数量 SELECT t.name,t.parent,t.term_id,count(1) as count ,COUNT( CASE WHEN p.post_status = 1 then 1 else null end ) as p1 # 1=已审核 ,COUNT( CASE WHEN p.post_status = 0 then 0 el

Mysql 列转行统计查询 、行转列统计查询

之前看过一篇博文写得非常好,看后就很容易让人理解,博文地址为:http://www.cnblogs.com/lhj588/p/3315876.html 最近在群里又碰到一个朋友说起,于是记录一下: 假设表名为t.表里有六个字段p1,p2,p3,s1,s2,s3 现在想得到 p1=100 时s1值的总和 p2=100 时s2值的总和 p3=100 时s3值的总和 创建表: CREATE TABLE `t` (   `id` int(11) NOT NULL AUTO_INCREMENT,   `s

mysql性能优化-慢查询分析、优化索引和配置

一.优化概述 二.查询与索引优化分析 1性能瓶颈定位 Show命令 慢查询日志 explain分析查询 profiling分析查询 2索引及查询优化 三.配置优化 1)      max_connections 2)      back_log 3)      interactive_timeout 4)      key_buffer_size 5)      query_cache_size 6)      record_buffer_size 7)      read_rnd_buffer

MySQL数据库存储引擎与数据库优化

存储引擎 (1)MySQL可以将数据以不同的技术存储在文件(内存)中,这种技术就成为存储引擎. 每种存数引擎使用不同的存储机制.索引技巧.锁定水平,最终提供广泛且不同的功能. (2)使用不同的存储引擎也可以说不同类型的表 (3)MySQL支持的存储引擎 MyISAM InnoDB Memory CSV Archive 查看数据表的创建语句: SHOW CREATE TABLE 表名 相关概念: (1).并发控制:一个人读数据,另外一个人在删除这个数据. 当多个连接对记录进行修改时保证数据的一致性

MySQL性能调优与架构设计——第9章 MySQL数据库Schema设计的性能优化

MySQL性能调优与架构设计——第9章 MySQL数据库Schema设计的性能优化 前言: 很多人都认为性能是在通过编写代码(程序代码或者是数据库代码)的过程中优化出来的,其实这是一个非常大的误区.真正影响性能最大的部分是在设计中就已经产生了的,后期的优化很多时候所能够带来的改善都只是在解决前妻设计所遗留下来的一些问题而已,而且能够解决的问题通常也比较有限.本章将就如何在 MySQL 数据库 Schema 设计的时候保证尽可能的高效,尽可能减少后期的烦恼. 9.1 高效的模型设计 最规范的就一定

MYSQL 分析表、检查表和优化表

1. 对表进行优化 ( 优化表主要作用是消除删除或者更新造成的空间浪费) 2. 对表进行分析(分析关键字的分布, 分析并存储MyISAM和BDB表中键的分布) 3. 对表进行检查(检查表的错误,并且为MyISAM更新键的统计内容) 4. 对表进行修复(修复被破坏的MyISAM表)   1.分析表   MySQL中使用ANALYZE TABLE语句来分析表,该语句的基本语法如下:   ANALYZE TABLE 表名1 [,表名2…] ; 使用ANALYZE TABLE分析表的过程中,数据库系统会

Mysql数据库调优和性能优化

1. 简介 在Web应用程序体系架构中,数据持久层(通常是一个关系数据库)是关键的核心部分,它对系统的性能有非常重要的影响.MySQL是目前使用最多的开源数据库,但是mysql数据库的默认设置性能非常的差,仅仅是一个玩具数据库.因此在产品中使用MySQL数据库必须进行必要的优化. 优化是一个复杂的任务,本文描述MySQL相关的数据库设计和查询优化,服务器端优化,存储引擎优化. 2. 数据库设计和查询优化 在MySQL Server性能调优中,首先要考虑的就是Database Schema设计,和