It will be faster to aggregate first and join later. Fewer join operations. Hence the subquery:
SELECT s.type, s.description, b.ct
FROM (
SELECT service, count(*) AS ct
FROM booking
WHERE status <> 'cancelled'
GROUP BY 1
HAVING count(*) > 2
) b
JOIN service s ON s.type = b.service;
Since you enforce referential integrity with a foreign key constraint and service
is defined NOT NULL
, you can as well use [INNER] JOIN
instead of a LEFT [OUTER] JOIN
in this query.
It would be cleaner and more efficient to use an enum
data type instead of VARCHAR(9)
for the status
column. Then you wouldn't need the CHECK
constraint either.
For best performance of this particular query, you could have a partial covering index (which would also profit from the enum
data type):
CREATE INDEX foo ON booking (service)
WHERE status <> 'cancelled';
Every index carries a maintenance cost, so only keep this tailored index if it actually makes your query faster (test with EXPLAIN ANALYZE
) and it is run often and / or important.