Kesalahan postgres [kolom harus muncul di klausa GROUP BY atau digunakan dalam fungsi agregat] ketika sub kueri digunakan

16

Saya punya dua meja employeedan phones. Seorang karyawan dapat memiliki 0 hingga n nomor telepon. Saya ingin mencantumkan nama karyawan dengan nomor telepon mereka. Saya menggunakan kueri di bawah ini yang berfungsi dengan baik.

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

masukkan deskripsi gambar di sini

Tabel karyawan mungkin berisi banyak baris. Saya hanya ingin mengambil beberapa karyawan sekaligus. Misalnya saya ingin menjemput 3 karyawan dengan nomor telepon mereka. Saya mencoba menjalankan kueri ini.

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

Tapi saya mendapatkan kesalahan ini. ERROR: column "employee.empname" must appear in the GROUP BY clause or be used in an aggregate function Satu-satunya perbedaan antara dua kueri adalah bahwa saya menggunakan sub kueri di yang terakhir untuk membatasi baris sebelum bergabung. Bagaimana cara mengatasi kesalahan ini?

Programmer
sumber

Jawaban:

21

Fitur Postgres untuk dapat menggunakan kunci utama tabel dengan GROUP BYdan tidak perlu menambahkan kolom lain dari tabel itu dalam GROUP BYklausa relatif baru dan hanya berfungsi untuk tabel dasar. Pengoptimal tidak (belum?) Cukup pintar untuk mengidentifikasi kunci utama untuk tampilan, ctes atau tabel turunan (seperti dalam kasus Anda).

Anda dapat menambahkan kolom yang Anda inginkan di SELECTdalam GROUP BYklausa:

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 ;

atau gunakan subquery (dan transfer yang GROUP BYada):

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 ;

yang juga dapat ditulis sebagai:

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 ;

Karena Anda berada di versi 9.3+. Anda juga dapat menggunakan LATERALgabungan:

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ᵀᴹ
sumber
@ypercude Terima kasih. Ini adalah cara yang sederhana dan bersih.
Programmer