Åtgärda LEFT JOIN
Detta borde fungera:
SELECT o.name AS organisation_name, count(e.id) AS total_used
FROM organisations o
LEFT JOIN exam_items e ON e.organisation_id = o.id
AND e.item_template_id = #{sanitize(item_template_id)}
AND e.used
GROUP BY o.name
ORDER BY o.name;
Du hade en LEFT [OUTER] JOIN
men den senare WHERE
villkoren gjorde att den fungerade som en vanlig [INNER] JOIN
.
Flytta villkoren/villkoren till JOIN
klausul för att få det att fungera som avsett. På så sätt sammanfogas endast rader som uppfyller alla dessa villkor i första hand (eller kolumner från höger tabellen är fylld med NULL). Som du hade det, testas sammanfogade rader för ytterligare förhållanden praktiskt taget efter LEFT JOIN
och tas bort om de inte går igenom, precis som med en vanlig JOIN
.
count()
returnerar aldrig NULL till att börja med. Det är ett undantag bland aggregerade funktioner i detta avseende. Därför aldrig vettigt, även med ytterligare parametrar. Manualen:COALESCE(COUNT(col))
Det bör noteras att förutom
count
, returnerar dessa funktioner ett nollvärde när inga rader är markerade.
Djärv betoning min. Se:
- Räkna antalet attribut som är NULL för en rad
count()
måste vara i en kolumn definierad NOT NULL
(som e.id
), eller där anslutningsvillkoret garanterar NOT NULL
(e.organisation_id
, e.item_template_id
, eller e.used
) i exemplet.
Sedan used
är typ boolean
, uttrycket e.used = true
är brus som brinner ner till bara e.used
.
Sedan o.name
är inte definierad UNIQUE NOT NULL
, kanske du vill GROUP BY o.id
istället (id
är PK) - om du inte avser för att vika rader med samma namn (inklusive NULL).
Aggregera först, gå med senare
Om de flesta eller alla rader med exam_items
räknas i processen, är denna motsvarande fråga vanligtvis betydligt snabbare/billigare:
SELECT o.id, o.name AS organisation_name, e.total_used
FROM organisations o
LEFT JOIN (
SELECT organisation_id AS id -- alias to simplify join syntax
, count(*) AS total_used -- count(*) = fastest to count all
FROM exam_items
WHERE item_template_id = #{sanitize(item_template_id)}
AND used
GROUP BY 1
) e USING (id)
ORDER BY o.name, o.id;
(Detta förutsätter att du inte vill vika rader med samma namn som nämnts ovan - det typiska fallet.)
Nu kan vi använda den snabbare/enklare count(*)
i underfrågan, och vi behöver ingen GROUP BY
i den yttre SELECT
.
Se:
- Flera array_agg()-anrop i en enda fråga