SQL Fun

grantm on 2006-03-30T08:44:43

I recently needed to query a database for a table of statistics like this:

city      | subscribers | males | females | full | trial
----------+-------------+-------+---------+------+-------
New York  |           7 |     4 |       3 |    3 |     4
Eketahuna |           1 |     1 |       0 |    1 |     0
Paris     |           4 |     3 |       1 |    1 |     3
London    |           4 |     3 |       1 |    2 |     2

It's obviously pretty easy to count one thing. For example, total number of subscribers per city:

SELECT city, count(*)
FROM subscriber
GROUP BY city

But how do you count multiple different things in a single query? I discovered today that some of my colleagues weren't familiar with this trick, so I thought I'd share it here.

The trick is to use sum() instead of count() and then to craft an expression to be sum'd which evaluates to 1 for the rows you want to count and zero for the ones you don't:

SELECT
  city, 
  count(*) AS subscribers,
  sum(
      CASE WHEN sex = 'M' THEN 1 ELSE 0 END
  ) as males,
  sum(
      CASE WHEN sex = 'F' THEN 1 ELSE 0 END
  ) as females,
  sum(
      CASE WHEN subscription_type = 'Full'  THEN 1 ELSE 0 END
  ) as full,
  sum(
      CASE WHEN subscription_type = 'Trial' THEN 1 ELSE 0 END
  ) as trial
FROM subscriber
GROUP BY city;

I believe the CASE is ANSI standard SQL, it works for me in PostgreSQL anyway :-)


Very cool

Aristotle on 2006-03-30T09:44:44

Thanks for sharing! That is neat.

Re: SQL Fun

broquaint on 2006-03-30T16:32:23

That looks like the beginnings of an SQL Crosstab ...

Or use nullif, for less typing

merlyn on 2006-03-30T16:45:32

Replace
sum(
      CASE WHEN sex = 'M' THEN 1 ELSE 0 END
)
with
count(nullif(sex = 'M', false))
This says, "return null if sex = m is false", and counts those. It's a bit inverted on the logic, but far less typing, and you're saying "count" again, which is really what you're doing.