count(distinct 慢查询语句如何改写?

时间:2021-01-29 14:32:04
昨天得到一个慢查询的解决思路,今天又找了一个困扰更长时间的sql
mysql 在linux 下 版本5.0以上 
当SessionLog 数量小的左右的时候,这个sql 执行时间速度还可以的 
select  count(distinct(ul.userId)) number,      
CASE        WHEN ul.lastaccess>curdate() THEN 1     
WHEN (ul.lastaccess>subdate(curdate(),1) and ul.lastaccess<curdate()) THEN 2          
WHEN (ul.lastaccess>date_Sub(curdate(), INTERVAL DAYOFMONTH(curdate())-1 day)) THEN 3     
 WHEN (ul.lastaccess>date_sub(date_Sub(curdate(), INTERVAL DAYOFMONTH(curdate())-1 day),INTERVAL 1 month) and ul.lastaccess<DATE_SUB(sysdate(),INTERVAL 1 month)) THEN 4        
 ELSE 5  END as Interval 

from  SessionLog ul,User u  where   u.id = ul.userid group by Interval;


但是现在几个月来,SessionLog 增长到300万,发现慢,高达43秒,实在是太慢。  而且这个表几乎每天必用,一天用好多次的。 
我已经对这个表加了索引 ,问该如何才能提升sql的速度?? 

我在按照昨天的方法进行加临时表 用union all联合进行查询加快查询速度。
但是count(distinct  感觉改写难度要大很多?

5 个解决方案

#1


和上次的一样。除了今天的,其余的统计每天晚上先生成。

#2


有点困惑 count(distinct(ul.userId)) 

WHEN (ul.lastaccess>subdate(curdate(),1) and ul.lastaccess <curdate()) THEN  等
还是头疼不知道如何转化?

#3


你要统计的不过是

本日,昨日,本月,上月
有多少不同名的用户登录。

你每天晚上定时 12:00am 生成这个数据 ( 昨日上月)
然后只需要统计 今天本月的就行了!

建议基于 lastaccess  的索引

select 
(select count(distinct userid) from SessionLog where lastaccess>=curdate()) as `number`,
1 as `Interval`
union all
select 
(select count(distinct userid) from SessionLog where lastaccess>=select curdate()-INTERVAL (day(curdate())-1) day ) as `number`,
3 as `Interval`
union all
select * from xxxxxxxxxx

#4


明白了 大致写如下

select  count(distinct(ul.userId)) number,      
CASE        WHEN ul.lastaccess>curdate() THEN 1    
WHEN (ul.lastaccess>subdate(curdate(),1) and ul.lastaccess <curdate()) THEN 2          
WHEN (ul.lastaccess>date_Sub(curdate(), INTERVAL DAYOFMONTH(curdate())-1 day)) THEN 3    
WHEN (ul.lastaccess>date_sub(date_Sub(curdate(), INTERVAL DAYOFMONTH(curdate())-1 day),INTERVAL 1 month) and ul.lastaccess <DATE_SUB(sysdate(),INTERVAL 1 month)) THEN 4        
ELSE 5  END as Interval 

from  SessionLog ul,User u  where  u.id = ul.userid 
and ul.lastaccess<'2009-11-01'

union all
select  count(distinct(ul.userId)) number,      
CASE        WHEN ul.lastaccess>curdate() THEN 1    
WHEN (ul.lastaccess>subdate(curdate(),1) and ul.lastaccess <curdate()) THEN 2          
WHEN (ul.lastaccess>date_Sub(curdate(), INTERVAL DAYOFMONTH(curdate())-1 day)) THEN 3    
WHEN (ul.lastaccess>date_sub(date_Sub(curdate(), INTERVAL DAYOFMONTH(curdate())-1 day),INTERVAL 1 month) and ul.lastaccess <DATE_SUB(sysdate(),INTERVAL 1 month)) THEN 4        
ELSE 5  END as Interval 

from  SessionLog ul,User u  where  u.id = ul.userid 
and ul.lastaccess>'2009-11-01 00:00:00'

group by Interval

解决  感谢 楼上

#5


上面的语句测试是ok的 

count(distinct  可以替换为  group by id
数据库设计其实可以改写的哦  (但项目已经上线 不要随便更改)

#1


和上次的一样。除了今天的,其余的统计每天晚上先生成。

#2


有点困惑 count(distinct(ul.userId)) 

WHEN (ul.lastaccess>subdate(curdate(),1) and ul.lastaccess <curdate()) THEN  等
还是头疼不知道如何转化?

#3


你要统计的不过是

本日,昨日,本月,上月
有多少不同名的用户登录。

你每天晚上定时 12:00am 生成这个数据 ( 昨日上月)
然后只需要统计 今天本月的就行了!

建议基于 lastaccess  的索引

select 
(select count(distinct userid) from SessionLog where lastaccess>=curdate()) as `number`,
1 as `Interval`
union all
select 
(select count(distinct userid) from SessionLog where lastaccess>=select curdate()-INTERVAL (day(curdate())-1) day ) as `number`,
3 as `Interval`
union all
select * from xxxxxxxxxx

#4


明白了 大致写如下

select  count(distinct(ul.userId)) number,      
CASE        WHEN ul.lastaccess>curdate() THEN 1    
WHEN (ul.lastaccess>subdate(curdate(),1) and ul.lastaccess <curdate()) THEN 2          
WHEN (ul.lastaccess>date_Sub(curdate(), INTERVAL DAYOFMONTH(curdate())-1 day)) THEN 3    
WHEN (ul.lastaccess>date_sub(date_Sub(curdate(), INTERVAL DAYOFMONTH(curdate())-1 day),INTERVAL 1 month) and ul.lastaccess <DATE_SUB(sysdate(),INTERVAL 1 month)) THEN 4        
ELSE 5  END as Interval 

from  SessionLog ul,User u  where  u.id = ul.userid 
and ul.lastaccess<'2009-11-01'

union all
select  count(distinct(ul.userId)) number,      
CASE        WHEN ul.lastaccess>curdate() THEN 1    
WHEN (ul.lastaccess>subdate(curdate(),1) and ul.lastaccess <curdate()) THEN 2          
WHEN (ul.lastaccess>date_Sub(curdate(), INTERVAL DAYOFMONTH(curdate())-1 day)) THEN 3    
WHEN (ul.lastaccess>date_sub(date_Sub(curdate(), INTERVAL DAYOFMONTH(curdate())-1 day),INTERVAL 1 month) and ul.lastaccess <DATE_SUB(sysdate(),INTERVAL 1 month)) THEN 4        
ELSE 5  END as Interval 

from  SessionLog ul,User u  where  u.id = ul.userid 
and ul.lastaccess>'2009-11-01 00:00:00'

group by Interval

解决  感谢 楼上

#5


上面的语句测试是ok的 

count(distinct  可以替换为  group by id
数据库设计其实可以改写的哦  (但项目已经上线 不要随便更改)