Postgres: Berbeda tetapi hanya untuk satu kolom

120

Saya memiliki tabel di pgsql dengan nama (memiliki lebih dari 1 juta baris), tetapi saya juga memiliki banyak duplikat. Saya pilih 3 bidang: id, name, metadata.

Saya ingin memilihnya secara acak dengan ORDER BY RANDOM()dan LIMIT 1000, jadi yang saya lakukan ini adalah banyak langkah untuk menghemat memori dalam skrip PHP saya.

Tetapi bagaimana saya bisa melakukannya sehingga hanya memberi saya daftar yang tidak memiliki duplikat dalam nama.

Misalnya [1,"Michael Fox","2003-03-03,34,M,4545"]akan dikembalikan tapi tidak [2,"Michael Fox","1989-02-23,M,5633"]. Bidang nama adalah yang paling penting dan harus unik dalam daftar setiap kali saya memilih dan harus acak.

Saya mencoba dengan GROUP BY name, tapi itu mengharapkan saya untuk memiliki id dan metadata di GROUP BYjuga atau dalam fungsi aggragate, tetapi saya tidak ingin mereka entah bagaimana difilter.

Adakah yang tahu cara mengambil banyak kolom tetapi hanya melakukan yang berbeda pada satu kolom?

NovumCoder
sumber

Jawaban:

226

Untuk melakukan perbedaan hanya pada satu (atau n) kolom:

select distinct on (name)
    name, col1, col2
from names

Ini akan mengembalikan salah satu baris yang berisi nama. Jika Anda ingin mengontrol baris mana yang akan dikembalikan, Anda perlu memesan:

select distinct on (name)
    name, col1, col2
from names
order by name, col1

Akan mengembalikan baris pertama saat dipesan oleh col1.

distinct on:

SELECT DISTINCT ON (ekspresi [, ...]) hanya mempertahankan baris pertama dari setiap set baris di mana ekspresi yang diberikan bernilai sama. Ekspresi DISTINCT ON diinterpretasikan menggunakan aturan yang sama seperti untuk ORDER BY (lihat di atas). Perhatikan bahwa "baris pertama" dari setiap set tidak dapat diprediksi kecuali ORDER BY digunakan untuk memastikan bahwa baris yang diinginkan muncul pertama kali.

Ekspresi DISTINCT ON harus cocok dengan ekspresi ORDER BY paling kiri. Klausa ORDER BY biasanya akan berisi ekspresi tambahan yang menentukan prioritas baris yang diinginkan dalam setiap grup DISTINCT ON.

Clodoaldo Neto
sumber
Tangkapan bagus saat memesan. Saya tidak memasukkannya karena mereka menyebutkan ingin memesan secara acak, tetapi penting untuk disebutkan.
Craig Ringer
Apakah order by namedibutuhkan? Akankah itu menghasilkan hasil yang berbeda dengan order by col1?
Elliot Chance
1
@ Elliot ya nameperlu. Periksa distinct ondi manual.
Clodoaldo Neto
1
Saya berharap tim TSQL dapat memberikan cara yang bijaksana untuk melakukan ini.
JTW
Harap tambahkan referensi
Ogaga Uzoh
17

Adakah yang tahu cara mengambil banyak kolom tetapi hanya melakukan yang berbeda pada satu kolom?

Anda ingin yang DISTINCT ONklausa .

Anda tidak memberikan data sampel atau kueri lengkap jadi saya tidak punya apa-apa untuk ditunjukkan kepada Anda. Anda ingin menulis sesuatu seperti:

SELECT DISTINCT ON (name) fields, id, name, metadata FROM the_table;

Ini akan mengembalikan kumpulan baris yang tidak dapat diprediksi (tetapi tidak "acak"). Jika Anda ingin membuatnya dapat diprediksi, tambahkan ORDER BYjawaban per Clodaldo. Jika Anda ingin membuatnya benar-benar acak, Anda pasti menginginkannya ORDER BY random().

Craig Ringer
sumber
Perhatikan saja dengan klausa DISTINCT ON ini, Anda hanya dapat ORDER OLEH hal yang sama + lebih banyak. Jadi jika Anda mengatakan DISTINCT ON (name) Anda harus ORDER BY name lalu apa pun yang Anda inginkan. Hampir tidak ideal.
Kevin Parker
Kevin, Anda bisa menggunakan CTE atau subquery-in-FROM dan ORDER BY di kueri luar
Craig Ringer
Ya, dan perhatikan kinerjanya ... Seluruh kemungkinan hasil dari ruang indeks akan dicari. Itu mengubah apa yang bisa menjadi kueri 10-20ms dengan indeks yang tepat menjadi 900ms hanya karena posgres tidak dapat menangani perbedaan / urutan yang berbeda. Tidak masalah apa pun urutan kueri luarnya, itu akan menggunakan indeks dari subkueri dalam untuk menemukan kecocokan terlebih dahulu, lalu mengurutkan ulang. Senang melakukan biaya konsultasi untuk solusi nyata untuk masalah kita di dba.stackexchange.com/questions/260852/…
Kevin Parker
4
SELECT NAME,MAX(ID) as ID,MAX(METADATA) as METADATA 
from SOMETABLE
GROUP BY NAME
David Jashi
sumber
2
Sekadar peringatan: itu mungkin tidak mengembalikan nilai ID atau nilai metadata yang dimiliki "bersama"
a_horse_with_no_name
@Novum Tidak. Artinya, cat mengambil nilai id dari salah satu baris Michael dan metadata dari baris lain seperti yang diminta untuk maxes Michael.
Clodoaldo Neto
Ya, itu sangat tergantung pada penggunaan OP data nyata, yang sama sekali tidak saya ketahui. Anda mungkin perlu menggunakan MIN atau apa pun. Baru saja ditunjukkan, bagaimana Anda bisa memasukkan bidang bukan pada GROUP BYklausa.
David Jashi
Ini bukan solusi yang baik karena nilai yang berbeda dari baris yang berbeda akan tercampur.
Elliot Chance