对于所有聚合函数,不仅是 rank(),更通用的解决方案是在 over() 子句中按“arrival_time is not null”进行分区。这将导致所有空的arrival_time 行被放置到同一个组中并给予相同的排名,使非空行仅相对于彼此进行排名。
为了一个有意义的例子,我模拟了一个比初始问题集有更多行的 CTE。请原谅宽行,但我认为它们更好地对比不同的技术。
with dinner_show_up("person", "arrival_time", "restaurant") as (values
('Dave' , 7, 'in_and_out')
,('Mike' , 2, 'in_and_out')
,('Bob' , null, 'in_and_out')
,('Peter', 3, 'in_and_out')
,('Jane' , null, 'in_and_out')
,('Merry', 5, 'in_and_out')
,('Sam' , 5, 'in_and_out')
,('Pip' , 9, 'in_and_out')
)
select
person
,case when arrival_time is not null then rank() over ( order by arrival_time) end as arrival_rank_without_partition
,case when arrival_time is not null then rank() over (partition by arrival_time is not null order by arrival_time) end as arrival_rank_with_partition
,case when arrival_time is not null then percent_rank() over ( order by arrival_time) end as arrival_pctrank_without_partition
,case when arrival_time is not null then percent_rank() over (partition by arrival_time is not null order by arrival_time) end as arrival_pctrank_with_partition
from dinner_show_up
此查询对arrival_rank_with/without_partition 给出相同的结果。但是,percent_rank() 的结果确实不同:without_partition 是错误的,范围从 0% 到 71.4%,而 with_partition 正确地给出了 pctrank() 的范围从 0% 到 100%。
同样的模式也适用于 ntile() 聚合函数。
为了排名,它通过将所有空值与非空值分开来工作。这确保了 Jane 和 Bob 被排除在 0% 到 100% 的百分位排名之外。
|person|arrival_rank_without_partition|arrival_rank_with_partition|arrival_pctrank_without_partition|arrival_pctrank_with_partition|
+------+------------------------------+---------------------------+---------------------------------+------------------------------+
|Jane |null |null |null |null |
|Bob |null |null |null |null |
|Mike |1 |1 |0 |0 |
|Peter |2 |2 |0.14 |0.2 |
|Sam |3 |3 |0.28 |0.4 |
|Merry |4 |4 |0.28 |0.4 |
|Dave |5 |5 |0.57 |0.8 |
|Pip |6 |6 |0.71 |1.0 |