Napraw LEFT JOIN
To powinno działać:
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;
Masz LEFT [OUTER] JOIN
ale późniejszy WHERE
warunki sprawiły, że zachowywał się jak zwykły [INNER] JOIN
.
Przenieś warunki do JOIN
klauzulę, aby działała zgodnie z przeznaczeniem. W ten sposób w pierwszej kolejności łączone są tylko wiersze, które spełniają wszystkie te warunki (lub kolumny od prawej tabele są wypełnione NULL). Tak jak wcześniej, połączone rzędy są testowane pod kątem dodatkowych warunków praktycznie po LEFT JOIN
i usuwane, jeśli nie przejdą, tak jak w przypadku zwykłego JOIN
.
count()
na początku nigdy nie zwraca wartości NULL. Jest to wyjątek wśród funkcji agregujących pod tym względem. Dlatego nigdy ma sens, nawet przy dodatkowych parametrach. Instrukcja:COALESCE(COUNT(col))
Należy zauważyć, że z wyjątkiem count
, te funkcje zwracają wartość null, gdy nie zaznaczono żadnych wierszy.
Moje odważne podkreślenie. Zobacz:
- Policz liczbę atrybutów, które mają wartość NULL dla wiersza
count()
musi znajdować się w zdefiniowanej kolumnie NOT NULL
(np. e.id
) lub gdzie warunek łączenia gwarantuje NOT NULL
(e.organisation_id
, e.item_template_id
lub e.used
) w przykładzie.
Ponieważ used
to typ boolean
, wyrażenie e.used = true
jest hałasem, który spala się tylko do e.used
.
Od o.name
nie jest zdefiniowany UNIQUE NOT NULL
, możesz chcieć GROUP BY o.id
zamiast tego (id
będąc PK) - chyba że zamierzasz do składania wierszy o tej samej nazwie (w tym NULL).
Najpierw agreguj, dołącz później
Jeśli większość lub wszystkie wiersze exam_items
są liczone w procesie, to równoważne zapytanie jest zazwyczaj znacznie szybsze / tańsze:
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;
(Przy założeniu, że nie chcesz składać wierszy o tej samej nazwie, jak wspomniana powyżej - typowy przypadek).
Teraz możemy użyć szybszego / prostszego count(*)
w podzapytaniu i nie potrzebujemy GROUP BY
w zewnętrznym SELECT
.
Zobacz:
- Wiele wywołań array_agg() w jednym zapytaniu