2013-08-12 29 views
9

Tôi đang viết truy vấn SQL bằng cách sử dụng PostgreSQL cần xếp hạng những người "đến" tại một số địa điểm. Tuy nhiên, không phải ai cũng đến. Tôi đang sử dụng chức năng cửa sổ rank() để tạo cấp bậc đến, nhưng ở những nơi thời gian đến là không, thay vì trả về xếp hạng không, hàm tổng hợp rank() chỉ xử lý chúng như thể chúng đến sau mọi người khác. Những gì tôi muốn xảy ra là những chương trình không có được một thứ hạng của NULL thay vì xếp hạng này.Bỏ qua các giá trị null trong chức năng cửa sổ postgresql rank()

Đây là một ví dụ. Giả sử tôi có một bảng dinner_show_up trông giống như sau:

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

Bob không bao giờ xuất hiện. Truy vấn Tôi đang viết sẽ là:

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

Và kết quả sẽ là

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

Những gì tôi muốn xảy ra thay vì là thế này:

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

Trả lời

10

Chỉ cần sử dụng một tuyên bố case xung quanh số 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

Hoàn hảo - cảm ơn! –

+0

+1 Thú vị, trong xếp hạng SQL Server sẽ xử lý null trước khi không null và xếp hạng sẽ có khoảng cách, trông giống như postgres xử lý nulls sau khi không null hoặc không đếm chúng ở tất cả? –

+3

@RomanPekar Bạn có thể xác định có hay không bạn muốn NULL lần đầu tiên hoặc cuối cùng trong ORDER BY của hàm RANK() (hoặc truy vấn thông thường của khóa học) -> 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

Đầu tiên - bạn cần thay đổi 'union' hoặc' union all'. Nếu không postgres sẽ lãng phí rất nhiều thời gian trên serching cho bản sao. Secong - truy vấn này liên quan đến 2 lần quét bảng - nó sẽ vẫn không thành công. –

7

Một giải pháp tổng quát hơn cho tất cả các hàm tổng hợp, không chỉ xếp hạng(), là phân vùng theo 'arrival_time không null' trong mệnh đề over(). Điều đó sẽ gây ra tất cả các hàng đến_time null được đặt vào cùng một nhóm và được xếp hạng giống nhau, để lại các hàng không rỗng được xếp hạng tương đối chỉ với nhau.

Vì lợi ích của một ví dụ có ý nghĩa, tôi đã giả lập CTE có nhiều hàng hơn tập hợp vấn đề nội bộ. Xin vui lòng tha thứ cho các hàng rộng, nhưng tôi nghĩ rằng họ tốt hơn so với các kỹ thuật khác nhau.

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 

Truy vấn này đưa ra kết quả tương tự cho arrival_rank_with/without_partition. Tuy nhiên, kết quả cho percent_rank() khác nhau: không có_partition sai, từ 0% đến 71,4%, trong khi with_partition cung cấp chính xác cho pctrank() từ 0% đến 100%.

Mẫu này cũng áp dụng cho hàm tổng hợp ntile().

Nó hoạt động bằng cách tách tất cả giá trị null khỏi giá trị không null cho mục đích xếp hạng. Điều này đảm bảo rằng Jane và Bob được loại trừ khỏi xếp hạng phần trăm từ 0% đến 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

Câu trả lời rất hay và chi tiết. Đặc biệt là bao gồm việc làm thế nào để có được thứ hạng phần trăm chính xác. Cảm ơn! –

Các vấn đề liên quan