数据:
语文,60
数学,70
语文,0
语文,10
语文,20
数学,50
语文,50
数学,40
英语,52
英语,51
英语,59
英语,97
英语,98
英语,100
数学,80
数学,90
英语,61
结果要求如下:
语文,0,1
语文,10,1
语文,20,1
数学,40,2
数学,50,2
语文,50,3
英语,51,4
英语,52,4
英语,59,4
语文,60,5
英语,61,6
数学,70,7
数学,80,7
数学,90,7
英语,97,8
英语,98,8
英语,100,8
思路:
观察数据可知,需要对数据进行升序排序,排序之后的数据当科目发生变化时,后面的数字就加1, 所以可以用开窗函数lag(col,n,default_val),得到前一行数据,再判断科目名称是否相等,如果相等就返回0,不相同就返回1。最后进行开窗的sum()。
具体操作:
1.按成绩升序排序
select
subject,
score,
lag(subject) over(order by score asc) before_name
from stu_score;
结果如下:
subject score before_name
语文 0 NULL
语文 10 语文
语文 20 语文
数学 40 语文
数学 50 数学
语文 50 数学
英语 51 语文
英语 52 英语
英语 59 英语
语文 60 英语
英语 61 语文
数学 70 英语
数学 80 数学
数学 90 数学
英语 97 数学
英语 98 英语
英语 100 英语
2.判断当前科目名称与前一行名称是否相同,如果相等就返回0,不相同就返回1
select
subject,
score,
before_name,
if( before_name=subject ,0,1) rn
from(
select
subject,
score,
lag(subject) over(order by score asc) before_name
from stu_score
) tmp;
结果如下:
subject score before_name rn
语文 0 NULL 1
语文 10 语文 0
语文 20 语文 0
数学 40 语文 1
数学 50 数学 0
语文 50 数学 1
英语 51 语文 1
英语 52 英语 0
英语 59 英语 0
语文 60 英语 1
英语 61 语文 1
数学 70 英语 1
数学 80 数学 0
数学 90 数学 0
英语 97 数学 1
英语 98 英语 0
英语 100 英语 0
3.使用sum(),开窗,进行累加
select
subject,
score,
before_name,
sum(if( before_name=subject ,0,1)) over(order by score asc)rn
from(
select
subject,
score,
lag(subject) over(order by score asc) before_name
from stu_score
) tmp;
结果如下:
subject score before_name rn
语文 0 NULL 1
语文 10 语文 1
语文 20 语文 1
数学 40 语文 2
数学 50 数学 3
语文 50 数学 3
英语 51 语文 4
英语 52 英语 4
英语 59 英语 4
语文 60 英语 5
英语 61 语文 6
数学 70 英语 7
数学 80 数学 7
数学 90 数学 7
英语 97 数学 8
英语 98 英语 8
英语 100 英语 8
问题:
数学 50 数学 3 应该为 数学 50 数学 2
原因:
开窗范围默认是:
When ORDER BY is specified with missing WINDOW clause, the WINDOW specification defaults to RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW.
此处RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,开窗范围为第一行到当前行,所以最开始的窗口里只有第一行数据,subject与前一行科目名称不相同,所以rn为1,sum()之后结果为1;窗口是慢慢累积的,所以接下来就是第一行和第二行,第二行科目名称和前一行科目名称相同,所以rn为0,sum=1+0=1,以此类推。
但range是根据值来开窗的,所以遇到两个成绩相同时,这两个都在一个窗口里,所以50这个值对应的sum=1+1+1=3
解决:
按照物理的行来开窗:ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
4.最终结果
select
subject,
score,
before_name,
sum(if( before_name=subject ,0,1)) over(order by score asc ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)rn
from(
select
subject,
score,
lag(subject) over(order by score asc) before_name
from stu_score
) tmp;
结果如下:
subject score before_name rn
语文 0 NULL 1
语文 10 语文 1
语文 20 语文 1
数学 40 语文 2
数学 50 数学 2
语文 50 数学 3
英语 51 语文 4
英语 52 英语 4
英语 59 英语 4
语文 60 英语 5
英语 61 语文 6
数学 70 英语 7
数学 80 数学 7
数学 90 数学 7
英语 97 数学 8
英语 98 英语 8
英语 100 英语 8