2013-08-12 5 views
9

Sto scrivendo una query SQL utilizzando PostgreSQL che ha bisogno di classificare le persone che "arrivano" in una certa posizione. Non tutti però arrivano. Sto usando una funzione finestra rank() per generare ranghi di arrivo, ma nei luoghi in cui l'ora di arrivo è nullo, piuttosto che restituire un rango nullo, la funzione di aggregazione rank() li tratta come se fossero arrivati ​​dopo tutti gli altri. Quello che voglio succedere è che questi no-show ottengono un punteggio di NULL invece di questo rango imputato.Ignorando valori nulli in in un rango postgresql() funzione di finestra

Ecco un esempio. Supponiamo che io sono un tavolo dinner_show_up che assomiglia a questo:

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

Bob non si presenta. La query che sto scrivendo sarebbe:

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

E il risultato sarà

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

Quello che voglio che accada invece è questo:

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

risposta

10

Basta usare una dichiarazione case intorno 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

Perfetto - grazie! –

+0

+1 È interessante notare che, in SQL Server verrà rango elaborare i valori nulli prima non nulli e rango avranno gap, si presenta come Postgres elaborazione nulli dopo non null o non li contano a tutti? –

+3

@RomanPekar È possibile definire se si desidera o meno NULL prima o l'ultima in ORDER BY della RANK() funzione (o query regolari ovviamente) -> 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

Prima - devi cambiare 'union' o' union all'. Altrimenti postgres sprecherà un sacco di tempo in serching per i duplicati. Secong: questa query richiede 2 scansioni di tabelle: sarà ancora inefficace. –

7

Una soluzione più generale per tutte le funzioni di aggregazione, non solo rank(), è la partizione per 'arrival_time non è nulla' nella clausola over(). Ciò farà sì che tutte le righe di orario di arrivo nullo vengano posizionate nello stesso gruppo e assegnate allo stesso rango, lasciando le righe non nulle ad essere classificate relative solo l'una all'altra.

Ai fini di un esempio significativo, ho schernito un CTE avente più righe il problema proposto intial. Per favore perdonare le file larghe, ma penso che contrastino meglio le diverse tecniche.

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 

Questa query fornisce gli stessi risultati per arrival_rank_with/without_partition. Tuttavia, i risultati per percent_rank() differiscono: without_partition è errato, va dallo 0% al 71.4%, mentre with_partition dà correttamente pctrank() che va dallo 0% al 100%.

Questo stesso schema si applica anche alla funzione di aggregazione ntile().

Funziona separando tutti i valori nulli da valori non nulli ai fini della classifica. Ciò garantisce che Jane e Bob siano esclusi dalla classifica dei percentili dallo 0% al 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

Risposta molto buona e dettagliata. Soprattutto come l'inclusione di come ottenere la classifica percentuale corretta. Grazie! –