sql sum排序_最全SQL窗口函数指南—数据分析面试必备

8ea82cb6851b7f11b344dd4e98f03b89.png

在笔者比较早期的面试经历中,面试官经常会出一些 SQL 题,而其中最常见的就是那些需要用 窗口函数 来解题的。

后来在笔者作为面试官面试候选人时,也是常常把窗口函数作为考察点。所以掌握好 SQL 窗口函数还是很必要的。

f8fcc191cb035b8aa00ff5e494b6c348.png

接下来我们按照顺序来介绍各种窗口函数。

一、组内排序函数:Rank/Dense_Rank/Row_Number

组内排序,我们常常用于取分组内排序前N/后N的记录,或先分组排序然后根据序号关联组内前一条或后一条记录。

Row_Number() over(partition by col1 order by col2):数字相同序号不重复,序号最大值等于总记录数;

Rank() over(partition by col1 order by col2):数字相同序号会重复,重复值后会跳过某些序号,序号最大值仍等于总记录数;

Dense_Rank() over(partition by col1 order by col2):数字相同序号会重复,重复值后不跳过某些序号,序号最大值会小于记录数。

示例,假定有如下一张表 test,执行下述 SQL 输出结果:
IDNameSal
1a10
2a12
3b13
4b12
5a14
6a15
7a13
8b11
9a16
10b17
11a14
Select 
   name, sal, 
   row_number() over(partition by name orderby sal desc) rnk1,
   rank() over(partition by name order by sal desc) rnk2,
   dense_rank() over(partition by name order by sal desc) rnk3
From  test
NameSalrnk1rnk2rnk3
b17111
b13222
b12333
b11444
a16111
a15222
a14333
a14433
a13554
a12665
a10776

二、累计计数、求和函数:SUM/AVG/MIN/MAX

这类函数应用非常广泛。包括不使用 group by 即可分组聚合、分组内滚动聚合从第一行到当前行或分组内滚动聚合前N行到后N行。

以 sum 为例,它的函数公式为:sum() over(partition by col1 order by col2 rows between xxx and yyy)。

下面我们来看看这个例子,假设有如下日志表 log:

cookie1 2015-04-10 1
cookie1 2015-04-11 5
cookie1 2015-04-12 7
cookie1 2015-04-13 3
cookie1 2015-04-14 2
cookie1 2015-04-15 4
cookie1 2015-04-16 4

以下述代码来介绍各种使用场景:

select 
 cookieid, pv,
 -- 从起点到当前行
 sum(pv) over(partition by cookieid order by created) as pv1, 
 -- 从起点到当前行,结果同pv1
 sum(pv) over(partition by cookieid order by created rows between unbounded preceding and current row) as pv2,
 -- 分组内所有行
 sum(pv) over(partition by cookieid) pv3,
-- 当前行往前3行 -> 当前行
 sum(pv) over(partition by cookieid order by created rows between 3 preceding and current row) pv4,
-- 当前行往前3行 -> 当前行往后1行
 sum(pv) over(partition by cookieid order by created rows between 3 preceding and 1 following) pv5,
-- 当前行往后所有行
 sum(pv) over(partition by cookieid order by created rows between current row and unbounded following) as pv6
from log

运行结果如下:

1c42515c520a8d4e62e4d580e6c8fb67.png

使用说明:

  1. 如果不指定rows between,默认为从起点到当前行;
  2. 如果不指定order by,那么将分组内所有值累加;
  3. row between 子句,也叫window子句:
preceding 往前;
following 往后;
current row 当前行;
unbounded 起点/终点:UNBOUNDED PRECEDING:表示从前面的起点, UNBOUNDED FOLLOWING:表示到后面的终点。

4. Avg、Count、Max、Min 等函数与 Sum 用法一样。

三、分桶函数:Ntile

分桶可用于便捷选择前/后N分之几的数据。

将有序的数据集平均分配到指定数量的桶内,并将桶号分配给每一条记录。如果不能平均分配,则优先分配给较小编号的桶,并且各个桶中能放的行数最多差1。

如,取上述示例 test 表 Sal 排名前50%的订单记录:

Select *
From(
 Select Id, Name, sal, 
    Ntile(2) over(partition by name order by sal desc) nt
 From test
)t where t.nt=1

四、前后平移函数:Lag/Lead

前后平移可快捷计算同比、环比值;

Lag(col, n, DEFAULT) 用于统计窗口内往上第n行值;

Lead(col, n, DEFAULT) 用于统计窗口内往下第n行值, 与LAG相反。

小 tips: default省略,则默认为NULL; 不需显式进行手动分组排序,在使用函数 LAG/LEAD 时最终呈现记录为自动排序。

示例:

如某张有 doctorid, patientid, add_time 3个字段的表,下列 2段 SQL 运行结果分别如下:
select 
 doctorid, patientid, add_time, 
 Lag(patientid, 1) over(partition by doctorid order by add_time) patient_bf
from stats.bi.nestle_patient 

23e649c0c6497d3b851c627a21e02430.png
select 
 doctorid, patientid, add_time, 
 Lead(patientid, 1, 5188786) over(partition by doctorid order by add_time) patient_af
from stats.bi.nestle_patient 

68f1e3c722409f34f2086f81a2fadf8b.png

五、分组取最早、最迟函数:First_Value/Last_Value

分组取最早、最迟函数可用于取用户的首次行为时间、最后一次行为时间,计算生命周期。

First_Value():分组内排序后,获得组内当前行往前的首个值;

Last_Value():分组内排序后,获得组内当前行往前的最后一个值。

如某张有 doctorid, patientid, add_time 3个字段的表,下列 2段 SQL 运行结果分别如下:
select 
 doctorid, patientid, add_time, 
 First_Value(patientId) over(partition by doctorId order by add_time) first_patient,
 Last_Value(patientId) over(partition by doctorId order by add_time) last_patient,
 First_Value(patientId) over(partition by doctorId order by add_time desc) first_patient_desc
from stats.bi.nestle_patient

d0a416dca12a2e26f109617e2e2198ad.png

六、序列分析函数:CUME_DIST/ PERCENT_RANK

Cume_Dist:小于等于当前值的行数/分组内总行数。应用场景统计,用于收入订单前多少的排名,或者订单数 pk 百分比。注意:没有 partition ,所有数据分到同一组。

Percent_Rank:分组内当前行的RANK值-1/分组内总行数-1。暂时没有想到应用场景。

SELECT 
 year, 
 doctorId,
 orders,
 CUME_DIST() OVER(ORDER BY orders) AS rn1,
 CUME_DIST() OVER(PARTITION BY year ORDER BY orders desc) AS rn2 
FROM(
   select date_trunc('year', payTime) year, doctorId, count(distinct id) orders
   from subscriptionorder
   where paystatus=2
   group by doctorId, date_trunc('year', payTime)
)

参考链接:http://www.cnblogs.com/wujin/p/6051768.html

参考资料:https://www.cnblogs.com/skyEva/p/5730531.html


以上为,SQL窗口函数。下一篇:SQL 索引及索引引发的查询优化。敬请期待。

希望对各位分析师老铁有帮助,欢迎评论区讨论。最后还请各位看官收藏不忘点赞。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值