使用子查询可提升 COUNT DISTINCT 速度 50 倍

Count distinct是SQL分析时的祸根

首先:如果你有一个大的且能够容忍不精确的数据集,那像HyperLogLog这样的概率计数器应该是你最好的选择。但对于需要快速、精准答案的查询,一些简单的子查询可以节省你很多时间。

让我们以我们一直使用的一个简单查询开始:哪个图表的用户访问量最大?

select
   dashboards.name,
   count(distinct time_on_site_logs.user_id)
from time_on_site_logs
join dashboards on time_on_site_logs.dashboard_id = dashboards.id
group by name
order by count desc

首先,我们假设user_id和dashboard_id上已经设置了索引,且有比图表和用户数多得多的日志条目。

一千万行数据时,查询需要48秒。要知道原因让我们看一下SQL解析:

它慢是因为数据库遍历了所有日志以及所有的图表,然后join它们,再将它们排序,这些都在真正的group和分组和聚合工作之前。

先聚合,然后Join

group-聚合后的任何工作代价都要低,因为数据量会更小。group-聚合时我们不需使用dashboards.name,我们也可以先在数据库上做聚集,在join之前:

select
  dashboards.name,
  log_counts.ct
from dashboards
join (
  select
    dashboard_id,
    count(distinct user_id) as ct
  from time_on_site_logs
   group by dashboard_id) as log_counts
 on log_counts.dashboard_id = dashboards.id
order by log_counts.ct desc 

现在查询运行了20秒,提升了2.4倍。再次通过解析来看一下原因:

正如设计的,group-聚合在join之前。而且,额外的我们可以利用time_on_site_logs表里的索引。

首先,缩小数据集
 
我们可以做的更好。通过在整个日志表上group-聚合,我们处理了数据库中很多不必要的数据。Count distinct为每个group生成一个哈希——在本次环境中为每个dashboard_id——来跟踪哪些bucket中的哪些值已经检查过。
 
我们可以预先计算差异,而不是处理全部数据,这样只需要一个哈希集合。然后我们在此基础上做一个简单的聚集即可。

select
  dashboards.name,
  log_counts.ct
 from dashboards
 join (
  select distinct_logs.dashboard_id,
  count(1) as ct
  from (
    select distinct dashboard_id, user_id
    from time_on_site_logs
  ) as distinct_logs
  group by distinct_logs.dashboard_id
) as log_counts
 on log_counts.dashboard_id = dashboards.id
order by log_counts.ct desc 

我们采取内部的count-distinct-group,然后将数据拆成两部分分成两块。第一块计算distinct (dashboard_id, user_id) 。第二块在它们基础上运行一个简单group-count。跟上面一样,最后再join。

呵呵,大发现:这样只需要0.7秒!这比上面的查询快28倍,比原来的快了68倍。
 
通常,数据大小和类型很重要。上面的例子受益于基数中没多少换算。distinct (user_id, dashboard_id)相对于数据总量来说数量也很少。不同的对数越多,用来group和计数的唯一数据就越多——代价便会越来越大。
 
下一次遇到长时间运行的count distinct时,尝试一些子查询来减负吧。

时间: 2024-08-06 09:08:26

使用子查询可提升 COUNT DISTINCT 速度 50 倍的相关文章

【T-SQL基础】03.子查询

本系列[T-SQL基础]主要是针对T-SQL基础的总结. [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础]02.联接查询 [T-SQL基础]03.子查询 [T-SQL基础]04.表表达式 [T-SQL基础]05.集合运算 [T-SQL基础]06.透视.逆透视.分组集 [T-SQL基础]07.数据修改 [T-SQL基础]08.事务和并发 [T-SQL基础]09.可编程对象 ----------------------------------------------------

数据库(子查询、主键、外键)

-------子查询------------select bumen,COUNT(*)as 人数 from haha group by bumen having COUNT(*)>=5select MAX(nianling) from haha where bumen='销售部'select *from haha where bumen='销售部'and nianling=56--汇总select *from haha where nianling = (select MAX(nianling)

SQL 基础之使用子查询检索数据(二十二)

多列子查询 where (manager_id, department_id) in 子查询 100 90 102 60 124 50 主查询的每行都与多行和多列的子查询进行比较 列的比较 多列的比较,包含子查询可以是: 不成对比较 成对比较 成对比较子查询1.显示与员工名为"John"同部门且同一个经理的其它员工信息 select employee_id, manager_id, department_id from empl_demo where (manager_id, depa

单表查询: where group by 分组 having distinct 去重 order by 排序 limit 多表查询 子查询 连表查询

今日内容 表查询 单表查询: where group by 分组 having distinct 去重 order by 排序 limit 多表查询 子查询 连表查询 单表查询 前期表准备 create table emp( id int not null unique auto_increment, name varchar(20) not null, sex enum('male','female') not null default 'male', #大部分是男的 age int(3) u

mongo中的高级查询之聚合操作(distinct,count,group)

1.distinct的实现: db.consumerecords.distinct("userId"):键值去重  类似于mysql中的 select distinct userId from consumerecords db.consumerecords.distinct("userId",{act:"charge"}):过滤之后去重,类似于mysql中的select distinct userId from consumerecords w

php查询mysql时,报超出内存错误(select count(distinct))时

学时服务器查询教练所带人数时,使用select count(distinct(u_STRNO))时报超出内存错误.后参考“mysqld-nt: Out of memory解决方法”http://jingyan.baidu.com/article/020278116b428d1bcd9ce568.html?qq-pf-to=pcqq.c2c 修改参数: key_buffer            = 512K    #global buffer   => key_buffer           

数据库 day60,61 Oracle入门,单行函数,多表查询,子查询,事物处理,约束,rownum分页,视图,序列,索引

1.    oracle介绍 ORACLE数据库系统是美国ORACLE公司(甲骨文)提供的以分布式数据库为核心的一组软件产品,是目前最流行的客户/服务器(CLIENT/SERVER)或B/S体系结构的数据库之一.比如SilverStream就是基于数据库的一种中间件.ORACLE数据库是目前世界上使用最为广泛的数据库管理系统,作为一个通用的数据库系统,它具有完整的数据管理功能:作为一个关系数据库,它是一个完备关系的产品:作为分布式数据库它实现了分布式处理功能.但它的所有知识,只要在一种机型上学习

MySQL里面的子查询

一.子查询定义 定义: 子查询允许把一个查询嵌套在另一个查询当中. 子查询,又叫内部查询,相对于内部查询,包含内部查询的就称为外部查询. 子查询可以包含普通select可以包括的任何子句,比如:distinct. group by.order by.limit.join和union等:但是对应的外部查询必须是以下语句之一:select.insert.update.delete.set或 者do. 子查询的位置: select 中.from 后.where 中.group by 和order by

Sql server2005 优化查询速度50个方法小结

Sql server2005 优化查询速度50个方法小结 Sql server2005优化查询速度51法查询速度慢的原因很多,常见如下几种,大家可以参考下. I/O吞吐量小,形成了瓶颈效应.  没有创建计算列导致查询不优化.  内存不足.  网络速度慢.  查询出的数据量过大(可以采用多次查询,其他的方法降低数据量).  锁或者死锁(这也是查询慢最常见的问题,是程序设计的缺陷).  sp_lock,sp_who,活动的用户查看,原因是读写竞争资源.  返回了不必要的行和列.  查询语句不好,没有