Advanced · 18 min
Conditional aggregation
Compute several filtered measures in one grouped query.
Sample database
CREATE TABLE groups (id INTEGER PRIMARY KEY, label TEXT NOT NULL);
CREATE TABLE members (id INTEGER PRIMARY KEY, name TEXT NOT NULL, city TEXT, group_id INTEGER REFERENCES groups(id));
CREATE TABLE sessions (id INTEGER PRIMARY KEY, member_id INTEGER NOT NULL REFERENCES members(id), topic TEXT NOT NULL, minutes INTEGER NOT NULL CHECK(minutes > 0), completed INTEGER NOT NULL CHECK(completed IN (0, 1)), day TEXT NOT NULL);
INSERT INTO groups VALUES (1, 'Morning'), (2, 'Evening');
INSERT INTO members VALUES (1, 'Ada', 'Oslo', 1), (2, 'Bo', 'Rome', 1), (3, 'Cy', NULL, 2), (4, 'Dee', 'Oslo', 2);
INSERT INTO sessions VALUES
(1, 1, 'Travel', 20, 1, '2026-01-01'),
(2, 1, 'Food', 10, 0, '2026-01-02'),
(3, 2, 'Travel', 30, 1, '2026-01-01'),
(4, 2, 'Music', 15, 1, '2026-01-03'),
(5, 3, 'Music', 25, 0, '2026-01-02'),
(6, 3, 'Travel', 15, 1, '2026-01-04');Add only matching values
A CASE expression inside SUM can turn unmatched rows into zero. This keeps several metrics in one grouped result. Consider how missing values and empty groups should be represented before choosing the ELSE branch.
SELECT member_id, SUM(CASE WHEN completed = 1 THEN minutes ELSE 0 END) AS completed_minutes FROM sessions GROUP BY member_id ORDER BY member_id;Output
member_id | completed_minutes 1 | 20 2 | 45 3 | 15
Calculate a rate safely
Use a real operand to avoid integer division and NULLIF to avoid dividing by zero. The left join keeps members without activity; their undefined completion rate remains NULL instead of pretending to be a measured zero.
SELECT m.name, 1.0 * SUM(CASE WHEN s.completed = 1 THEN 1 ELSE 0 END) / NULLIF(COUNT(s.id), 0) AS rate FROM members AS m LEFT JOIN sessions AS s ON s.member_id = m.id GROUP BY m.id, m.name ORDER BY m.id;Output
name | rate Ada | 0.5 Bo | 1 Cy | 0.5 Dee | NULL
Put it into practice
- Read the sample tables and predict the result before running the query.
- Solve the task, compare the returned rows, and explain why the solution works.
Try it yourself
Code runs on this device. When you are signed in, drafts sync to your account.
Run queries on a fresh sample database. Table changes last for this run only; your query draft is saved separately. Results show column names followed by rows. NULL means a missing value.
Apply what you learned
Return completed minutes per member, using zero for each unfinished session before summing.
Use a conditional expression inside the aggregate.
Show solution
SELECT member_id, SUM(CASE WHEN completed = 1 THEN minutes ELSE 0 END) AS completed_minutes FROM sessions GROUP BY member_id ORDER BY member_id;Only completed sessions contribute their duration to the total.