2012-03-13 111 views
8

考慮:SQL頻率分佈查詢數與組的範圍,包括0計數

table 'thing': 

age 
--- 
3.4 
3.4 
10.1 
40 
45 
49 

我要計算的東西,每10年範圍內的數量,例如,

age_range | count 
----------+------- 
     0 |  2 
     10|  1 
     20|  0 
     30|  0 
     40|  3 

這個查詢接近:

SELECT FLOOR(age/10) as age_range, COUNT(*) 
FROM thing 
GROUP BY FLOOR(age/10) ORDER BY FLOOR(age/10); 

輸出:

age_range | count 
-----------+------- 
     0 |  1 
     1 |  2 
     4 |  3 

但是,它沒有顯示出具有0計數的範圍。我怎樣才能修改查詢,以便它也顯示了0計數之間的範圍?

我發現了類似的計算範圍的問題,一些爲0計數,但它們涉及必須指定每個範圍(或者將範圍硬編碼到查詢中,或者將範圍放在表中)。我寧願使用上面的通用查詢,我不必明確指定每個範圍(例如0-10,10-20,20-30,...)。我使用的是PostgreSQL 9.1.3。

有沒有辦法修改上面的簡單查詢以包含0計數?

類似:
Oracle: how to "group by" over a range?
Get frequency distribution of a decimal range in MySQL

回答

10

generate_series救援:

select 10 * s.d, count(t.age) 
from generate_series(0, 10) s(d) 
left outer join thing t on s.d = floor(t.age/10) 
group by s.d 
order by s.d 

搞清楚上限generate_series應該是微不足道的一個單獨的查詢,我只是用10作爲佔位符。

此:

generate_series(0, 10) s(d) 

基本上產生與其中包含的值從0至10(含)的單個列d稱爲s內嵌表。

如果需要,可以將兩個查詢(一個計算出範圍,一個計算計數)包裝到一個函數中。

+0

這很好。好的功能知道。 – Glenn 2012-03-13 02:46:05

+0

優雅的答案,@ mu_is_too_short!我試過了,它工作。正是我在找的東西。謝謝! – 2012-03-13 02:47:03

+0

@Glenn:是的,'generate_series'非常有用,它也可以處理時間戳,所以再見日曆表。 – 2012-03-13 05:44:53

1

您需要一些方法來發明年齡段的表格。行號通常很好地工作。對一張大桌子做一個笛卡爾產品以獲取大量數字。

WITH RANGES AS (
SELECT (rownum - 1) * 10 AS age_range 
    FROM (SELECT row_number() OVER() as rownum 
      FROM pg_tables 
     ) n 
     ,(SELECT ceil(max(age)/10) range_end 
      FROM thing 
     ) m 
    WHERE n. rownum <= range_end 
) 
SELECT r.age_range, COUNT(t.age) AS count 
    FROM ranges r 
    LEFT JOIN thing t ON r.age_range = FLOOR(t.age/10) * 10 
    GROUP BY r.age_range 
    ORDER BY r.age_range; 

編輯:畝太短有一個更優雅的答案,但如果你沒有在一個數據庫中的generate_series功能,... :)