In Redshift/Postgres, how to count rows that meet a condition?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In PostgreSQL and Amazon Redshift, conditional counting is done with COUNT combined with CASE WHEN, FILTER (WHERE) (Postgres 9.4+), or SUM with a boolean expression cast to integer. These approaches let you count rows meeting different criteria in a single query without multiple WHERE-filtered subqueries. Redshift supports most PostgreSQL conditional counting syntax but does not support FILTER (WHERE).
Basic COUNT with WHERE
This counts all rows matching the WHERE clause. But what if you need multiple conditional counts in one query?
COUNT with CASE WHEN
The most portable approach — works in PostgreSQL, Redshift, MySQL, SQL Server, and all SQL databases:
COUNT ignores NULL values, so when the CASE has no ELSE clause, non-matching rows produce NULL and are not counted.
Multiple Conditions
SUM with Boolean Expression
An alternative that leverages the fact that true casts to 1 and false to 0:
The SUM(CASE WHEN ... THEN 1 ELSE 0 END) pattern is equivalent to COUNT(CASE WHEN ... THEN 1 END) and works everywhere.
FILTER (WHERE) — PostgreSQL Only
PostgreSQL 9.4+ supports the FILTER clause for aggregate functions, which is cleaner:
FILTER (WHERE) works with any aggregate function (COUNT, SUM, AVG, MIN, MAX, ARRAY_AGG, etc.).
Conditional Count with GROUP BY
Counting Distinct Values Conditionally
Window Function with Conditional Count
Performance: Redshift-Specific Tips
Common Pitfalls
- Using FILTER (WHERE) in Redshift: The
FILTERclause is a PostgreSQL extension not supported by Redshift. Queries usingFILTERfail with a syntax error in Redshift. UseCOUNT(CASE WHEN ... THEN 1 END)instead, which works in both systems. - Using COUNT(*) with CASE instead of COUNT(expression):
COUNT(CASE WHEN cond THEN 1 END)works becauseCOUNTskips NULLs. If you accidentally addELSE 0, the count includes non-matching rows (since 0 is not NULL). Either omit theELSEwithCOUNT, or useSUMwithELSE 0. - Dividing counts without casting to float:
COUNT(completed) / COUNT(*)performs integer division and always returns 0 or 1. Cast toDECIMALor multiply by100.0first to get a percentage. - Not using GROUP BY with conditional counts: Conditional counts without
GROUP BYreturn a single row for the entire table. If you need per-category breakdowns, addGROUP BY categoryand include the column in theSELECT. - Counting NULLs:
COUNT(column)excludes NULL values;COUNT(*)includes them. When counting conditionally,CASE WHEN col IS NULL THEN 1 ENDis needed to count NULL rows, sinceCOUNT(col)silently skips them.
Summary
- Use
COUNT(CASE WHEN condition THEN 1 END)for portable conditional counting (works in Postgres, Redshift, MySQL, SQL Server) - Use
SUM(CASE WHEN condition THEN 1 ELSE 0 END)as an equivalent alternative - Use
FILTER (WHERE)in PostgreSQL 9.4+ for cleaner syntax (not supported in Redshift) - Combine with
GROUP BYfor breakdowns by category, time period, or other dimensions - Cast to
DECIMALbefore dividing counts to avoid integer division truncation - In Redshift, use sort keys on columns in
WHEREandCASE WHENclauses for performance

