Corregir el LEFT JOIN
Esto debería funcionar:
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;
Tenías un LEFT [OUTER] JOIN
pero el posterior WHERE
las condiciones lo hicieron actuar como un simple [INNER] JOIN
.
Mover la(s) condición(es) a JOIN
cláusula para que funcione según lo previsto. De esta forma, solo se unen en primer lugar las filas que cumplen todas estas condiciones (o las columnas de la derecha tabla se llenan con NULL). Como lo tenía, las filas unidas se prueban para condiciones adicionales virtualmente después el LEFT JOIN
y eliminados si no aprueban, al igual que con un simple JOIN
.
count()
nunca devuelve NULL para empezar. Es una excepción entre las funciones agregadas a este respecto. Por lo tanto, nunca tiene sentido, incluso con parámetros adicionales. El manual:COALESCE(COUNT(col))
Cabe señalar que excepto para count
, estas funciones devuelven un valor nulo cuando no se selecciona ninguna fila.
Énfasis en negrita mío. Ver:
- Cuente el número de atributos que son NULOS para una fila
count()
debe estar en una columna definida NOT NULL
(como e.id
), o donde la condición de unión garantiza NOT NULL
(e.organisation_id
, e.item_template_id
, o e.used
) en el ejemplo.
Desde used
es de tipo boolean
, la expresión e.used = true
es ruido que se reduce a solo e.used
.
Desde o.name
no está definido UNIQUE NOT NULL
, es posible que desee GROUP BY o.id
en su lugar (id
siendo el PK), a menos que intente para plegar filas con el mismo nombre (incluido NULL).
Agregue primero, únase después
Si la mayoría o todas las filas de exam_items
se cuentan en el proceso, esta consulta equivalente suele ser considerablemente más rápida/más económica:
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;
(Esto supone que no desea doblar filas con el mismo nombre como se mencionó anteriormente, el caso típico).
Ahora podemos usar el count(*)
más rápido/sencillo en la subconsulta, y no necesitamos GROUP BY
en el exterior SELECT
.
Ver:
- Múltiples llamadas a array_agg() en una sola consulta