sql >> Base de Datos >  >> RDS >> PostgreSQL

Consulta con LEFT JOIN que no devuelve filas para el conteo de 0

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, COALESCE(COUNT(col)) nunca tiene sentido, incluso con parámetros adicionales. El manual:

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