Aggregate FILTER
clause in Postgres 9.4+
Since Postgres 9.4 there is a clean and fast (SQL standard) way:
SELECT count(*) FILTER (WHERE score BETWEEN 0 AND 3) AS low
, count(*) FILTER (WHERE score BETWEEN 4 AND 7) AS mid
, count(*) FILTER (WHERE score BETWEEN 8 AND 10) AS high
, count(*) AS total
FROM foo;
total
adds up low
, mid
and high
, unless NULL or other values are involved.
Links:
Also read below.
Postgres 9.3-
There are a couple of techniques:
@Phil provided a common alternative with a CASE
statement (except for sum(1)
, which isn't the standard way). I like to use a shorter form:
SELECT count(score BETWEEN 0 AND 3 OR NULL) AS low
, count(score BETWEEN 4 AND 6 OR NULL) AS mid
, count(score BETWEEN 7 AND 10 OR NULL) AS high
, count(*) AS total
FROM foo;
If your values are as defined in your question (only 0
- 10
possible), simplify further:
SELECT count(score < 4 OR NULL) AS low
, count(score BETWEEN 4 AND 6 OR NULL) AS mid
, count(score > 6 OR NULL) AS high
, count(*) AS total
FROM foo;
A bit shorter, barely faster.
Subtle differences
There are subtle differences when compared to sum()
in Phil's answer:
It should be noted that except for count
, these functions return a
null value when no rows are selected. In particular, sum
of no rows
returns null, not zero as one might expect, ...
count(*)
is the standard way and a bit faster than sum(1)
. Again, null vs. 0 applies.
Either of these queries (including Phil's) counts null values for total
. If that's not desirable, use instead:
count(score) AS total_not_null
db<>fiddle here
Old sqlfiddle (pg 9.3)