MySQL中Avg()与If()函数组合(Avg(if()))的执行逻辑问询
问题背景
现有数据表如下:
| s.userid | c.userid | c.action |
|---|---|---|
| 3 | 3 | timeout |
| 3 | 3 | timeout |
| 7 | 7 | confirmed |
| 7 | 7 | confirmed |
| 7 | 7 | confirmed |
| 2 | 2 | timeout |
| 2 | 2 | confirmed |
需求为针对每个s.userid,计算公式:count(confirmed) / (count(confirmed) + count(timeout)),期望得到结果:
| s.userid | rate |
|---|---|
| 3 | 0 |
| 7 | 1 |
| 2 | 0.5 |
看到的解决方案SQL语句如下:
select s.user_id, avg(if(c.action = 'confirmed', 1, 0)) as rate from *tablename* group by s.user_id;
执行逻辑拆解
这条SQL的执行顺序可以拆成以下几步:
- 逐行转换值:先扫过数据表的每一行,用
if()函数对c.action做转换——如果当前行是confirmed,返回1;是timeout则返回0。比如userid=3的两行都返回0,userid=7的三行都返回1,userid=2的一行返回0、一行返回1。 - 按用户分组:把处理后的行按照
s.user_id分成三组,分别对应userid=3、7、2。 - 计算分组平均值:对每个分组里的0和1计算平均值。
- userid=3的分组是两个0,平均值为
(0+0)/2=0,刚好对应0个confirmed/(0+2个timeout)的结果; - userid=7的分组是三个1,平均值为
(1+1+1)/3=1,对应3个confirmed/(3+0个timeout); - userid=2的分组是一个0和一个1,平均值为
(0+1)/2=0.5,对应1个confirmed/(1+1个timeout)。
- userid=3的分组是两个0,平均值为
本质上,avg(if(条件,1,0))就是在计算满足条件的行数占分组总行数的比例,和你要的公式完全等价——因为count(confirmed)是满足条件的行数,count(confirmed)+count(timeout)是分组总行数,两者的比值就是这些0和1的平均值。
内容的提问来源于stack exchange,提问作者gollapudi sravani
相关产品推荐
相关产品推荐

