web-dev-qa-db-ja.com

サブクエリを使用すると、Postgresエラー[列はGROUP BY句に表示されるか、集計関数で使用される必要があります]

2つのテーブルemployeephonesがあります。従業員は0〜n個の電話番号を持つことができます。従業員の名前と電話番号をリストしたい。以下のクエリを使用して、問題なく動作します。

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

従業員テーブルには多数の行が含まれる場合があります。一度に一部の従業員のみを取得したい。たとえば、3人の従業員とその電話番号を取得したいとします。このクエリを実行しようとしています。

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

しかし、私はこのエラーを受け取ります。 ERROR: column "employee.empname" must appear in the GROUP BY clause or be used in an aggregate function 2つのクエリの唯一の違いは、結合する前に行を制限するために後者でサブクエリを使用していることです。このエラーを解決するにはどうすればよいですか?

18
Programmer

GROUP BYでテーブルの主キーを使用でき、GROUP BY句にそのテーブルの他の列を追加する必要がないPostgresの機能は比較的新しいもので、ベーステーブルでのみ機能します。オプティマイザーは(まだ?)(あなたの場合のように)ビュー、Ctes、または派生テーブルの主キーを識別するのに十分ではありません。

SELECTの必要な列をGROUP BY句に追加できます。

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 ;

またはサブクエリを使用します(そしてGROUP BYをそこに転送します):

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 ;

これは次のように書くこともできます。

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 ;

あなたはバージョン9.3+にいるので。 LATERAL結合を使用することもできます。

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ᵀᴹ