Misalkan kita memiliki tabel dengan empat kolom (a,b,c,d)
dari tipe data yang sama.
Apakah mungkin untuk memilih semua nilai yang berbeda di dalam data di kolom dan mengembalikannya sebagai satu kolom atau apakah saya harus membuat fungsi untuk mencapai ini?
postgresql
postgresql-performance
postgresql-9.4
distinct
Fabrizio Mazzoni
sumber
sumber
SELECT a FROM tablename UNION SELECT b FROM tablename UNION SELECT c FROM tablename UNION SELECT d FROM tablename ;
?UNION
Jawaban:
Pembaruan: Menguji semua 5 kueri dalam SQLfiddle dengan 100K baris (dan 2 kasus terpisah, satu dengan beberapa (25) nilai berbeda dan lainnya dengan banyak (sekitar nilai 25K).
Permintaan yang sangat sederhana adalah menggunakan
UNION DISTINCT
.Saya pikir akan lebih efisien jika ada indeks terpisah pada masing-masing dari empat kolom.Ini akan efisien dengan indeks terpisah pada masing-masing dari empat kolom, jika Postgres telah menerapkan optimisasi pemindaian indeks longgar , yang belum. Jadi kueri ini tidak akan efisien karena membutuhkan 4 pemindaian tabel (dan tidak ada indeks yang digunakan):Lain akan menjadi pertama
UNION ALL
dan kemudian digunakanDISTINCT
. Ini juga akan membutuhkan 4 scan tabel (dan tidak menggunakan indeks). Bukan efisiensi yang buruk ketika nilainya sedikit, dan dengan lebih banyak nilai menjadi yang tercepat dalam pengujian (tidak luas) saya:Jawaban lain telah memberikan lebih banyak opsi menggunakan fungsi array atau
LATERAL
sintaks. Permintaan Jack (187 ms, 261 ms
) memiliki kinerja yang masuk akal tetapi permintaan AndriyM tampaknya lebih efisien (125 ms, 155 ms
). Keduanya melakukan satu pemindaian berurutan dari tabel dan tidak menggunakan indeks apa pun.Sebenarnya hasil permintaan Jack sedikit lebih baik daripada yang ditunjukkan di atas (jika kita menghapus
order by
) dan dapat lebih ditingkatkan dengan menghapus 4 internaldistinct
dan hanya menyisakan yang eksternal.Akhirnya, jika - dan hanya jika - nilai yang berbeda dari 4 kolom relatif sedikit, Anda dapat menggunakan
WITH RECURSIVE
retas / optimisasi yang dijelaskan di halaman Pemindaian Indeks Lepas di atas dan menggunakan semua 4 indeks, dengan hasil yang sangat cepat! Diuji dengan baris 100K yang sama dan sekitar 25 nilai berbeda yang tersebar di 4 kolom (berjalan hanya dalam 2 ms!) Sedangkan dengan nilai berbeda 25K itu paling lambat dengan 368 ms:SQLfiddle
Untuk meringkas, ketika nilai-nilai yang berbeda sedikit, kueri rekursif adalah pemenang mutlak sementara dengan banyak nilai, nilai ke-2 saya, Jack (versi yang ditingkatkan di bawah) dan kueri AndriyM adalah yang berkinerja terbaik.
Penambahan yang terlambat, variasi pada kueri ke-1 yang meskipun memiliki operasi yang sangat berbeda, berkinerja jauh lebih baik daripada yang pertama dan hanya sedikit lebih buruk daripada yang ke-2:
dan Jack membaik:
sumber
Anda dapat menggunakan LATERAL, seperti dalam kueri ini :
Kata kunci LATERAL memungkinkan sisi kanan gabungan untuk referensi objek dari sisi kiri. Dalam kasus ini, sisi kanan adalah konstruktor VALUES yang membangun subset satu kolom dari nilai kolom yang ingin Anda masukkan ke dalam satu kolom. Query utama hanya mereferensikan kolom baru, juga menerapkan DISTINCT.
sumber
Agar jelas, saya akan menggunakan
union
seperti yang disarankan ypercube , tetapi juga dimungkinkan dengan array:Aku di sini
sumber
Terpendek
Versi ide Andriy yang kurang jelas hanya sedikit lebih panjang, tetapi lebih elegan dan lebih cepat.
Untuk banyak berbeda / beberapa nilai ganda:
Tercepat
Dengan indeks pada setiap kolom yang terlibat!
Untuk beberapa nilai duplikat yang berbeda / banyak :
Ini adalah varian rCTE lain, mirip dengan yang @ypercube sudah diposting , tapi saya menggunakan
ORDER BY 1 LIMIT 1
bukannyamin(a)
yang biasanya sedikit lebih cepat. Saya juga tidak memerlukan predikat tambahan untuk mengecualikan nilai NULL.Dan
LATERAL
bukannya subquery yang berkorelasi, karena lebih bersih (belum tentu lebih cepat).Penjelasan terperinci dalam jawaban masuk saya untuk teknik ini:
Saya memperbarui SQL Fiddle ypercube dan menambahkan milik saya ke daftar putar.
sumber
EXPLAIN (ANALYZE, TIMING OFF)
untuk memverifikasi kinerja keseluruhan terbaik? (Terbaik dari 5 untuk mengecualikan efek caching.)VALUES ...
lebih cepat daripadaunnest(ARRAY[...])
.LATERAL
tersirat untuk fungsi set-return dalamFROM
daftar.Anda bisa, tetapi ketika saya menulis dan menguji fungsi saya merasa salah. Ini adalah pemborosan sumber daya.
Cukup gunakan serikat pekerja dan lebih banyak pilih. Hanya keuntungan (jika ya), satu pemindaian tunggal dari tabel utama.
Dalam sql fiddle Anda perlu mengubah pemisah dari $ ke sesuatu yang lain, seperti /
sumber