最近老顾变得原来越咸鱼了,好久没去逛 CSDN 问答了,于是灵感枯竭,不知道该怎么水文章了。嘿嘿。
今天无意中又去逛了一圈,发现一个有点意思的小问题,那么就来分享一下吧 :https://ask.csdn.net/questions/7974639
sql语句问题
现在有三个字段 ping,lostrate,jitter,每个字段有个标准(ping>90,lostrate>0.01,jitter>30),超过这个标准就算超标了,表中有几条数据
要求当三个字段中的某一个超标的时候,算出其平均值,我写的sql语句如下:平均值为ping:110,lostrate:0.03,jitter:60
嗯,具体问题可以看看题主的原始内容。总之呢,就是提供了一些初始数据,然后给定了一些条件,想得出一个特定结果。不过由于题主的描述有点问题,不知道最终需求结果到底是个什么样,咱们就随便写写好了。
CSDN 文盲老顾的博客,https://blog.csdn.net/superwfei
老顾的个人社区,https://bbs.csdn.net/forums/bfba6c5031e64c13aa7c60eebe858a5f?category=10003&typeId=3364713
select 100 ping,.0 lostrate,10 jitter
union all select 120,0,10
union all select 30,.02,20
union all select 30,.04,20
union all select 30,0,50
union all select 30,0,70
根据题主的截图,老顾模拟一下这个初始数据,几个 union 即可。
ping>90,lostrate>0.01,jitter>30
看题主的意思是,任意一项数据符合上述三个条件之一,就算超标了,需要将超标的数据单独拿出来计算平均值。
所以,我们第一个方案,是直接得到汇总后的结果。
with t as (
select 100 ping,.0 lostrate,10 jitter
union all select 120,0,10
union all select 30,.02,20
union all select 30,.04,20
union all select 30,0,50
union all select 30,0,70
)
select count(0) 总数,sum(case when ping>90 then 1 else 0 end) [ping 超标数量]
,avg(case when ping>90 then ping else null end) [ping 超标平均值]
,sum(case when lostrate>0.01 then 1 else 0 end)
,avg(case when lostrate>0.01 then lostrate else null end)
,sum(case when jitter>30 then 1 else 0 end)
,avg(case when jitter>30 then jitter else null end)
from t
在这里,使用 null 不参与计算的特性,来实现超标部分的求平均方法。
因为想保留原有数据,其他数据需要附加在原有数据之后
所以,我们先使用开窗函数计算一下各个数据超标与不超标的平均值结果好了。
with t as (
select 100 ping,.0 lostrate,10 jitter
union all select 120,0,10
union all select 30,.02,20
union all select 30,.04,20
union all select 30,0,50
union all select 30,0,70
)
select *
,avg(ping) over(partition by (case when ping > 90 then 1 else 0 end))
,avg(lostrate) over(partition by (case when lostrate > .01 then 1 else 0 end))
,avg(jitter) over(partition by (case when jitter > 30 then 1 else 0 end))
from t
这里,老顾的开窗函数只用了 partition ,而没有使用 order ,这样的结果,就相当于每列数据单独 group 了一下,嘿嘿,偷懒的小窍门。
那么,现在的问题是,我们需要更加优化一下这个查询,非超标部分的平均值,我们就不计算了,其次,还要把超标的数量也列出来。
with t as (
select 100 ping,.0 lostrate,10 jitter
union all select 120,0,10
union all select 30,.02,20
union all select 30,.04,20
union all select 30,0,50
union all select 30,0,70
)
select *
,(case when ping > 90 then count(0) over(partition by (case when ping > 90 then 1 else 0 end)) else null end)
,(case when ping > 90 then avg(ping) over(partition by (case when ping > 90 then 1 else 0 end)) else null end)
,(case when lostrate > .01 then count(0) over(partition by (case when lostrate > .01 then 1 else 0 end)) else null end)
,(case when lostrate > .01 then avg(lostrate) over(partition by (case when lostrate > .01 then 1 else 0 end)) else null end)
,(case when jitter > 30 then count(0) over(partition by (case when jitter > 30 then 1 else 0 end)) else null end)
,(case when jitter > 30 then avg(jitter) over(partition by (case when jitter > 30 then 1 else 0 end)) else null end)
from t
直接用 case when 来设置条件,符合条件的,才进行汇总计算,就可以得到预定结果了
同样,这个结果想要变成方案一的汇总结果,也很方便,只要解这几个无名列给出列名,然后对这些列直接取 max 结果,就是汇总结果了。
因为不知道题主到底想要个什么结果,所以老顾就写了两个方案,应该总有一款适用于这个题主的需求。可惜老顾看到这个题目的时候,已经结题了,没办法提交老顾的答案了。
今天在做这个题目的时候,群里又有小伙伴问,count 是否必须跟着 group 走。。。。
嗯。。。。这个小伙伴是在刷力扣的数据库题,看到很多题目里都有 count 和 group 了。
但实际上,count、max、min、sum、avg 这几个,统称为聚合函数,而聚合函数可以结合分组使用,也可以单独使用,更可以结合开窗函数使用。
比如,本文的第一个方案,就是聚合函数单独使用的情况,这种情况,相当于所有数据默认带了一个 group by (相同数据列),也就是 group by 的分组,只有一个分组,所有数据都在这个分组内罢了。
再比如,本文的第二个方案,就是聚合函数结合开窗函数的使用方式,同样没有group,而是使用开窗函数的 partition 分区来代替了 group 分组。
最后就是,一定要注意,当列的值为 null 的时候,这一行的数据,不参与该列的所有计算,包括但不限于聚合函数,数学运算之类的。利用好这个特性,其实很多工作反而变的简单起来。