PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Consulta com LEFT JOIN não retornando linhas para contagem de 0

Corrigir o LEFT JOIN


Isso deve 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;

Você tinha um LEFT [OUTER] JOIN mas o posterior WHERE condições fizeram com que ele agisse como um simples [INNER] JOIN .
Mova a(s) condição(ões) para o JOIN cláusula para fazê-lo funcionar como pretendido. Dessa forma, apenas as linhas que atendem a todas essas condições são unidas em primeiro lugar (ou colunas da direita tabela são preenchidos com NULL). Como você fez, as linhas unidas são testadas para condições adicionais virtualmente depois o LEFT JOIN e removidos se não passarem, assim como com um simples JOIN .

count() nunca retorna NULL para começar. É uma exceção entre as funções agregadas a esse respeito. Portanto, COALESCE(COUNT(col)) nunca faz sentido, mesmo com parâmetros adicionais. O manual:

Deve-se observar que exceto para count , essas funções retornam um valor nulo quando nenhuma linha é selecionada.

Minha ênfase em negrito. Ver:
  • Conte o número de atributos que são NULL para uma linha

count() deve estar em uma coluna definida NOT NULL (como e.id ), ou onde a condição de junção garante NOT NULL (e.organisation_id , e.item_template_id , ou e.used ) no exemplo.

Desde used é tipo boolean , a expressão e.used = true é o ruído que se reduz a apenas e.used .

Desde o.name não está definido UNIQUE NOT NULL , você pode querer GROUP BY o.id em vez disso (id sendo o PK) - a menos que você intenha para dobrar linhas com o mesmo nome (incluindo NULL).

Agregue primeiro, junte-se depois


Se a maioria ou todas as linhas de exam_items são contados no processo, essa consulta equivalente é normalmente consideravelmente mais rápida / mais barata:
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;

(Isto supõe que você não deseja dobrar linhas com o mesmo nome mencionado acima - o caso típico.)

Agora podemos usar o count(*) mais rápido/simples na subconsulta e não precisamos de GROUP BY no SELECT externo .

Ver:
  • Várias chamadas array_agg() em uma única consulta