LEFT JOINを修正します
これは機能するはずです:
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;
LEFT [OUTER] JOINがありました しかし、後のWHERE 条件により、単純な[INNER] JOINのように動作しました 。
条件をJOINに移動します 意図したとおりに機能させるための句。このようにして、最初にこれらすべての条件を満たす行(または右の列)のみが結合されます。 テーブルはNULLで埋められます)。あなたがそれを持っていたように、結合された行は事実上後に追加の条件についてテストされます LEFT JOIN プレーンなJOINの場合と同様に、合格しなかった場合は削除されます 。
count() そもそもNULLを返すことはありません。この点では、集計関数の例外です。したがって、 決して 追加のパラメータがあっても、理にかなっています。マニュアル:COALESCE(COUNT(col))
countを除くことに注意してください 、行が選択されていない場合、これらの関数はnull値を返します。
大胆な強調鉱山。参照:
- 行に対してNULLである属性の数を数えます
count() NOT NULLで定義された列に存在する必要があります (e.idのように )、または結合条件がNOT NULLを保証する場合 (e.organisation_id 、e.item_template_id 、またはe.used )例では。
used以降 タイプはbooleanです 、式e.used = true e.usedだけに燃え尽きるノイズです 。
o.name以降 定義されていませんUNIQUE NOT NULL 、GROUP BY o.idが必要な場合があります 代わりに(id PKであること)-意図しない 同じ名前(NULLを含む)の行を折りたたむ。
最初に集約し、後で参加する
exam_itemsのほとんどまたはすべての行の場合 プロセスでカウントされる場合、この同等のクエリは通常、かなり高速/安価です:
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;
(これは、上記のような同じ名前の行を折りたたまないことを前提としています-一般的なケースです。)
これで、より高速でシンプルなcount(*)を使用できるようになりました。 サブクエリで、GROUP BYは必要ありません 外側のSELECT 。
参照:
- 1回のクエリで複数のarray_agg()を呼び出す