2013-08-12 23 views
9

私は、PostgreSQLを使用してSQLクエリを作成しています。このクエリは、ある場所に「到着」した人をランク付けする必要があります。しかし誰もが到着するわけではありません。私はrank()ウィンドウ関数を使用して到着ランクを生成していますが、到着時刻がnullの場所ではnullランクを返すのではなく、rank()集計関数は他のユーザーの後に到着したかのように扱います。私がしたいことは、これらのノー・ショーがこの帰属ランクの代わりにNULLのランクを取得するということです。postgresql rank()ウィンドウ関数のヌル値を無視する

ここは例です。私はこのようになりますテーブルdinner_show_upを持っていると仮定します。

| Person | arrival_time | Restaurant | 
+--------+--------------+------------+ 
| Dave |  7  | in_and_out | 
| Mike |  2  | in_and_out | 
| Bob | NULL  | in_and_out | 

Bobが現れることはありません。

select Person, 
     rank() over (partition by Restaurant order by arrival_time asc) 
       as arrival_rank 
from dinner_show_up; 

、結果は私が代わりに発生しますが、これは

| Person | arrival_rank | 
+--------+--------------+ 
| Dave |  2  | 
| Mike |  1  | 
| Bob |  3  | 

次のようになります:私は書いているクエリは次のようになり

| Person | arrival_rank | 
+--------+--------------+ 
| Dave |  2  | 
| Mike |  1  | 
| Bob |  NULL  | 

答えて

10

ちょうど周りcaseステートメントを使用しますthe rank()

select Person, 
     (case when arrival_time is not null 
      then rank() over (partition by Restaurant order by arrival_time asc) 
     end) as arrival_rank 
from dinner_show_up; 
+0

パーフェクト - ありがとう! –

+0

+1興味深いことに、SQL ServerのランクではNULLがなくなる前にNULLが処理され、ランクにはギャップがあります。ポストグルはNULLを処理していないか、またはそれらを数えません。 –

+3

@RomanPekar RANK()関数のORDER BY(または通常のクエリ)の最初または最後にNULLを入れるかどうかを定義できます - > http://www.postgresql.org/docs/current/static/ indexes-ordering.html – bma

0
select Person, 
    rank() over (partition by Restaurant order by arrival_time asc) 
      as arrival_rank 
from dinner_show_up 
where arrival_time is not null 
union 
select Person,NULL as arrival_rank 
from dinner_show_up 
where arrival_time is null; 
+0

まず、 'union'または' union all'を変更する必要があります。さもなければ、postgresは重複のためにserchingに多くの時間を無駄にします。 Secong - このクエリには2回のテーブルスキャンが含まれますが、それでもまだ不十分です。 –

7

rank()だけでなく、over()節の 'arrival_time is not null'によってパーティションを分割するのがより一般的な集計関数です。これにより、すべての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()の結果は異なります。with_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       | 
+0

非常に良い、詳細な答え。特にパーセントランクを正しく得る方法を含めるのが好きです。ありがとう! –

関連する問題