PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Abfrage mit LEFT JOIN, die keine Zeilen für die Anzahl 0 zurückgibt

Korrigieren Sie den LEFT JOIN

Das sollte funktionieren:

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;

Sie hatten einen LEFT [OUTER] JOIN aber das spätere WHERE Bedingungen ließen es sich wie ein einfacher [INNER] JOIN verhalten .
Verschieben Sie die Bedingung(en) in den JOIN Klausel, damit es wie beabsichtigt funktioniert. Auf diese Weise werden überhaupt nur Zeilen verbunden, die alle diese Bedingungen erfüllen (oder Spalten von rechts Tabelle sind mit NULL gefüllt). Wie Sie es hatten, werden verbundene Zeilen praktisch nach auf zusätzliche Bedingungen getestet der LEFT JOIN und entfernt, wenn sie nicht bestehen, genau wie bei einem einfachen JOIN .

count() gibt von vornherein nie NULL zurück. Diesbezüglich ist es eine Ausnahme unter den Aggregatfunktionen. Daher COALESCE(COUNT(col)) nie sinnvoll, auch mit zusätzlichen Parametern. Das Handbuch:

Es sollte beachtet werden, dass mit Ausnahme von count , geben diese Funktionen einen Nullwert zurück, wenn keine Zeilen ausgewählt sind.

Fette Hervorhebung von mir. Siehe:

  • Zählen Sie die Anzahl der Attribute, die für eine Zeile NULL sind

count() muss auf einer Spalte definiert sein NOT NULL (wie e.id ) oder wo die Join-Bedingung NOT NULL garantiert (e.organisation_id , e.item_template_id , oder e.used ) im Beispiel.

Seit used ist vom Typ boolean , der Ausdruck e.used = true ist Rauschen, das sich auf nur noch e.used niederbrennt .

Seit o.name ist nicht definiert UNIQUE NOT NULL , möchten Sie vielleicht GROUP BY o.id stattdessen (id der PK ist) - es sei denn, Sie beabsichtigen um Zeilen mit demselben Namen (einschließlich NULL) zu falten.

Erst aggregieren, später beitreten

Wenn die meisten oder alle Zeilen von exam_items dabei gezählt werden, ist diese äquivalente Abfrage typischerweise deutlich schneller / günstiger:

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;

(Dies setzt voraus, dass Sie Zeilen mit demselben Namen nicht wie oben erwähnt falten möchten - der typische Fall.)

Jetzt können wir das schnellere / einfachere count(*) verwenden in der Unterabfrage, und wir brauchen kein GROUP BY im äußeren SELECT .

Siehe:

  • Mehrere array_agg()-Aufrufe in einer einzigen Abfrage