it-swarm.com.de

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

Ich habe zwei Tabellen employee und phones. Ein Mitarbeiter kann 0 bis n Telefonnummern haben. Ich möchte die Namen der Mitarbeiter mit ihren Telefonnummern auflisten. Ich verwende 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

enter image description here

Die Mitarbeitertabelle kann eine große Anzahl von Zeilen enthalten. Ich möchte jeweils nur einige Mitarbeiter abrufen. 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 letzteren eine Unterabfrage verwende, um die Zeilen vor dem Beitritt zu begrenzen. Wie löse ich diesen Fehler?

18
Programmer

Die Funktion von Postgres, den Primärschlüssel einer Tabelle mit GROUP BY Verwenden zu können und die anderen Spalten dieser Tabelle nicht in der Klausel GROUP BY Hinzufügen zu müssen, ist relativ neu und funktioniert nur für base Tabellen. Der Optimierer ist (noch?) Nicht clever genug, um Primärschlüssel für Ansichten, ctes oder abgeleitete Tabellen zu identifizieren (wie in Ihrem Fall).

Sie können die gewünschten Spalten in SELECT in die Klausel GROUP BY 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 den GROUP BY dorthin):

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 ;

Da bist du in Version 9.3+. Sie können auch einen LATERAL Join 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 ;
22
ypercubeᵀᴹ