Repareer de LEFT JOIN
Dit zou moeten werken:
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;
Je had een LEFT [OUTER] JOIN
maar de latere WHERE
omstandigheden zorgden ervoor dat het zich gedroeg als een eenvoudige [INNER] JOIN
.
Verplaats de voorwaarde(n) naar de JOIN
clausule om het te laten werken zoals bedoeld. Op deze manier worden alleen rijen die aan al deze voorwaarden voldoen in eerste instantie samengevoegd (of kolommen van rechts tabel zijn gevuld met NULL). Zoals u het had, worden samengevoegde rijen virtueel na . getest op aanvullende voorwaarden de LEFT JOIN
en verwijderd als ze niet slagen, net als bij een gewone JOIN
.
count()
geeft om te beginnen nooit NULL terug. Het is in dit opzicht een uitzondering onder geaggregeerde functies. Daarom nooit is logisch, zelfs met extra parameters. De handleiding:COALESCE(COUNT(col))
Opgemerkt moet worden dat behalve voor
count
, retourneren deze functies een null-waarde als er geen rijen zijn geselecteerd.
Vetgedrukte nadruk van mij. Zie:
- Tel het aantal attributen dat NULL is voor een rij
count()
moet op een kolom staan gedefinieerd NOT NULL
(zoals e.id
), of waar de join-voorwaarde NOT NULL
. garandeert (e.organisation_id
, e.item_template_id
, of e.used
) in het voorbeeld.
Sinds used
is type boolean
, de uitdrukking e.used = true
is ruis die opbrandt tot slechts e.used
.
Sinds o.name
is niet gedefinieerd UNIQUE NOT NULL
, wilt u misschien GROUP BY o.id
in plaats daarvan (id
de PK zijn) - tenzij u van plan bent om rijen met dezelfde naam te vouwen (inclusief NULL).
Eerst samenvoegen, later meedoen
Als de meeste of alle rijen van exam_items
worden meegeteld, is deze equivalente zoekopdracht doorgaans aanzienlijk sneller / goedkoper:
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;
(Dit gaat ervan uit dat je geen rijen wilt vouwen met dezelfde naam zoals hierboven vermeld - het typische geval.)
Nu kunnen we de snellere / eenvoudigere count(*)
. gebruiken in de subquery, en we hebben geen GROUP BY
. nodig in de buitenste SELECT
.
Zie:
- Meerdere array_agg()-aanroepen in één query