Saya punya meja yang mirip penelepon ini 'pembuat'
cname | wmname | avg
--------+-------------+------------------------
canada | zoro | 2.0000000000000000
spain | luffy | 1.00000000000000000000
spain | usopp | 5.0000000000000000
Dan saya ingin memilih rata-rata maksimum untuk setiap cname.
SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname;
tapi saya akan mendapatkan kesalahan,
ERROR: column "makerar.wmname" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname;
jadi saya melakukan ini
SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname, wmname;
namun ini tidak akan memberikan hasil yang diinginkan, dan keluaran yang salah di bawah ini ditampilkan
cname | wmname | max
--------+--------+------------------------
canada | zoro | 2.0000000000000000
spain | luffy | 1.00000000000000000000
spain | usopp | 5.0000000000000000
Hasil yang sebenarnya seharusnya
cname | wmname | max
--------+--------+------------------------
canada | zoro | 2.0000000000000000
spain | usopp | 5.0000000000000000
Bagaimana saya bisa memperbaiki masalah ini?
Catatan: Tabel ini adalah LIHAT yang dibuat dari operasi sebelumnya.
sql
group-by
aggregate-functions
postgresql-9.1
RandomGuy
sumber
sumber
wmname="usopp"
diharapkan dan bukan misalnyawmname="luffy"
?Jawaban:
Ya, ini adalah masalah agregasi yang umum. Sebelum SQL3 (1999) , bidang yang dipilih harus muncul dalam
GROUP BY
klausa [*].Untuk mengatasi masalah ini, Anda harus menghitung agregat dalam sub-kueri dan kemudian bergabung dengan itu sendiri untuk mendapatkan kolom tambahan yang Anda perlu tampilkan:
Tetapi Anda juga dapat menggunakan fungsi jendela, yang terlihat lebih sederhana:
Satu-satunya hal dengan metode ini adalah ia akan menampilkan semua catatan (fungsi jendela tidak mengelompokkan). Tapi itu akan menunjukkan yang benar (yaitu maks. Di
cname
level)MAX
untuk negara di setiap baris, jadi terserah Anda:Solusinya, bisa dibilang kurang elegan, untuk menunjukkan satu-satunya
(cname, wmname)
tupel yang cocok dengan nilai maks, adalah:[*]: Cukup menarik, meskipun jenis spek memungkinkan untuk memilih bidang yang tidak dikelompokkan, mesin utama tampaknya tidak terlalu menyukainya. Oracle dan SQLServer tidak mengizinkan ini sama sekali. Mysql dulu mengizinkannya secara default, tetapi sekarang sejak 5.7 administrator perlu mengaktifkan opsi ini (
ONLY_FULL_GROUP_BY
) secara manual dalam konfigurasi server agar fitur ini didukung ...sumber
MAX
(lihat jawaban oleh @ ypercube, ada juga solusi lain dalam jawaban saya) tetapi tidak dengan cara Anda melakukannya. Periksa output yang diharapkan.avg
percname
) tetapi tidak membatasi baris hasil (seperti yang diinginkan OP). Lihat Hasil Sebenarnya harus paragraf dalam pertanyaan.ONLY_FULL_GROUP_BY
di MySQL 5.7 tidak mengaktifkan cara SQL menspesifikasikan standar ketika kolom dapat dihilangkan darigroup by
(atau membuat MySQL berperilaku seperti Postgres). Itu hanya kembali ke perilaku lama di mana MySQL mengembalikan hasil acak (= "tak tentu") sebagai gantinya.Di Postgres, Anda juga dapat menggunakan
DISTINCT ON (expression)
sintaks khusus :sumber
BY cname
?Masalah dengan menentukan bidang non-kelompok dan non-agregat dalam
group by
pemilihan adalah bahwa mesin tidak memiliki cara untuk mengetahui bidang rekaman mana yang harus dikembalikan dalam kasus ini. Apakah ini yang pertama? Apakah ini yang terakhir? Biasanya tidak ada catatan yang secara alami sesuai dengan hasil agregat (min
danmax
merupakan pengecualian).Namun, ada solusinya: buat bidang yang dibutuhkan juga teragregasi. Dalam posgres, ini harus bekerja:
Perhatikan bahwa ini menciptakan larik semua nama, dipesan oleh rata-rata, dan mengembalikan elemen pertama (array di postgres adalah berbasis 1).
sumber
Menggunakan
rank()
fungsi jendela :Catatan
Salah satu dari mereka akan mempertahankan beberapa nilai maks per grup. Jika Anda hanya ingin catatan tunggal per grup walaupun ada lebih dari satu catatan dengan rata-rata sama dengan maksimal Anda harus memeriksa jawaban @ ypercube.
sumber
Bagi saya, ini bukan tentang "masalah agregasi umum", tetapi hanya tentang permintaan SQL yang salah. Satu jawaban yang benar untuk "pilih rata-rata maksimum untuk setiap nama ..." adalah
Hasilnya adalah:
Hasil ini secara umum menjawab pertanyaan "Apa hasil terbaik untuk setiap kelompok?" . Kita melihat bahwa hasil terbaik untuk Spanyol adalah 5 dan untuk Kanada hasil terbaik adalah 2. Benar, dan tidak ada kesalahan. Jika kita perlu menampilkan wmname juga, kita harus menjawab pertanyaan: "Apa ATURAN untuk memilih wmname dari set yang dihasilkan?" Mari kita ubah sedikit data input untuk mengklarifikasi kesalahan:
Yang mengakibatkan yang Anda harapkan pada runnig query ini:
SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname;
? Harus ituspain+luffy
atauspain+usopp
? Mengapa? Tidak ditentukan dalam kueri cara memilih wmname "lebih baik" jika beberapa cocok, sehingga hasilnya juga tidak ditentukan. Itu sebabnya penerjemah SQL mengembalikan kesalahan - kueri tidak benar.Dengan kata lain, tidak ada jawaban yang benar untuk pertanyaan "Siapa yang terbaik dalam
spain
kelompok?" . Luffy tidak lebih baik dari usopp, karena usopp memiliki "skor" yang sama.sumber
SELECT cname, id, MAX(avg) FROM makerar GROUP BY cname;
yang memang memberikan kesalahan menyesatkan ini.Ini sepertinya berhasil juga
sumber
Saya baru-baru ini mengalami masalah ini, ketika mencoba menghitung menggunakan
case when
, dan menemukan bahwa mengubah urutanwhich
dancount
pernyataan memperbaiki masalah:Alih-alih menggunakan - di yang terakhir, di mana saya mendapat kesalahan bahwa apel dan jeruk harus muncul dalam fungsi agregat
sumber
which
pernyataan?