2012-03-13 9 views
8

Dane:SQL liczyć zakresy z grupą-by i zawierają 0 liczy

table 'thing': 

age 
--- 
3.4 
3.4 
10.1 
40 
45 
49 

Chcę policzyć rzeczy dla każdego zakresu 10-letniej, np

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

To zapytanie zbliża:

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

wyjściowa:

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

Jednak nie pokazuje mi zakresów, które mają 0 zliczeń. W jaki sposób mogę zmodyfikować zapytanie tak, aby pokazywało także zakresy między 0 a liczbą?

Znalazłem podobne pytania stackoverflow dla zliczania zakresów, niektóre dla 0 zliczeń, ale wiążą się z koniecznością określenia każdego zakresu (albo zakodowanie sztywne zakresów w zapytaniu, albo wprowadzenie zakresów w tabeli). Wolałbym używać ogólnego zapytania, takiego jak powyżej, w którym nie muszę wyraźnie określać każdego zakresu (np. 0-10, 10-20, 20-30, ...). Używam PostgreSQL 9.1.3.

Czy istnieje sposób modyfikacji powyższego prostego zapytania w celu uwzględnienia liczby 0?

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

Odpowiedz

10

generate_series na ratunek:

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 

zastanawianie się górną granicę dla generate_series powinny być trywialne z oddzielnym zapytania, po prostu stosować 10 jako zastępczy .

to:

generate_series(0, 10) s(d) 

zasadniczo generuje tablicę inline nazwie s z pojedynczej kolumny d który zawiera wartości od 0 do 10 (włącznie).

Można zawinąć dwa zapytania (jeden do określenia zakresu, jeden do obliczenia zliczeń) do funkcji, jeśli to konieczne.

+0

To miłe. Dobra funkcja do poznania. – Glenn

+0

Elegancka odpowiedź, @mu_is_too_short! Wypróbowałem to i zadziałało. Właśnie tego szukałem. Dzięki! –

+0

@Glenn: Tak, 'generate_series' jest szalenie użyteczna, może również pracować z sygnaturami czasowymi, więc do zobaczenia tabel kalendarza. –

1

Potrzebujesz sposobu na wymyślenie tabeli przedziałów wiekowych. Numer wiersza zwykle działa poprawnie. Wykonuj produkt kartezjański przy dużym stole, aby uzyskać wiele liczb.

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; 

EDIT: il jest zbyt krótkie, ma znacznie bardziej elegancki odpowiedź, ale jeśli nie ma funkcji generate_series na db ... :)