Postgres-Fehler [Spalte muss in der GROUP BY-Klausel erscheinen oder in einer Aggregatfunktion verwendet werden], wenn eine Unterabfrage verwendet wird

15

Ich habe zwei Tische employeeund phones. Ein Mitarbeiter kann 0 bis n Telefonnummern haben. Ich möchte die Namen der Mitarbeiter mit ihren Telefonnummern auflisten. Ich benutze die folgende Abfrage, die gut läuft.

SELECT empname,array_agg(phonenumber) AS phonenumbers 
FROM employee LEFT OUTER JOIN phones ON employee.empid = phones.empid
GROUP BY employee.empid

Bildbeschreibung hier eingeben

Die Mitarbeitertabelle enthält möglicherweise eine große Anzahl von Zeilen. Ich möchte nur einige Mitarbeiter gleichzeitig abholen. Zum Beispiel möchte ich 3 Mitarbeiter mit ihren Telefonnummern abrufen. Ich versuche, diese Abfrage auszuführen.

SELECT empname,array_agg(phonenumber) AS phonenumbers 
FROM 
(SELECT * FROM employee ORDER BY empname LIMIT 3 OFFSET 0) AS employee 
LEFT OUTER JOIN phones ON employee.empid = phones.empid
GROUP BY employee.empid

Aber ich bekomme diesen Fehler. ERROR: column "employee.empname" must appear in the GROUP BY clause or be used in an aggregate function Der einzige Unterschied zwischen zwei Abfragen besteht darin, dass ich in letzterer Unterabfrage verwende, um die Zeilen vor dem Beitritt einzuschränken. Wie löse ich diesen Fehler?

Programmierer
quelle

Antworten:

20

Die Funktion von Postgres, den Primärschlüssel einer Tabelle verwenden zu können GROUP BYund nicht die anderen Spalten dieser Tabelle in der GROUP BYKlausel hinzufügen zu müssen, ist relativ neu und funktioniert nur für Basistabellen. Der Optimierer ist (noch?) Nicht clever genug, um Primärschlüssel für Views, Ctes oder abgeleitete Tabellen zu identifizieren (wie in Ihrem Fall).

Sie können die gewünschten Spalten SELECTin die GROUP BYKlausel einfügen:

SELECT e.empname, array_agg(p.phonenumber) AS phonenumbers 
FROM 
(SELECT * FROM employee ORDER BY empname LIMIT 3 OFFSET 0) AS e 
LEFT OUTER JOIN phones AS p ON e.empid = p.empid
GROUP BY e.empid, e.empname 
ORDER BY e.empname ;

oder verwenden Sie eine Unterabfrage (und übertragen Sie die GROUP BYdort):

SELECT e.empname,
       (SELECT array_agg(p.phonenumber) 
        FROM phones AS p
        WHERE e.empid = p.empid
       ) AS phonenumbers 
FROM 
(SELECT * FROM employee ORDER BY empname LIMIT 3 OFFSET 0) AS e 
ORDER BY e.empname ;

was auch geschrieben werden könnte als:

SELECT e.empname,
       (SELECT array_agg(p.phonenumber) 
        FROM phones AS p
        WHERE e.empid = p.empid
       ) AS phonenumbers 
FROM employee AS e
ORDER BY e.empname LIMIT 3 OFFSET 0 ;

Seit du in Version 9.3+ bist. Sie können auch einen LATERALJoin verwenden:

SELECT e.empname,
       p.phonenumbers 
FROM 
   (SELECT * FROM employee ORDER BY empname LIMIT 3 OFFSET 0) AS e
LEFT JOIN LATERAL
   (SELECT array_agg(phonenumber) AS phonenumbers
    FROM phones 
    WHERE e.empid = phones.empid
   ) AS p ON TRUE 
ORDER BY e.empname ;
ypercubeᵀᴹ
quelle
@ypercude Danke. Dies ist eine einfache und saubere Methode.
Programmierer