Saya ingin pemilihan acak baris di PostgreSQL, saya mencoba ini:
select * from table where random() < 0.01;
Tetapi beberapa yang lain merekomendasikan ini:
select * from table order by random() limit 1000;
Saya punya meja yang sangat besar dengan 500 juta baris, saya ingin cepat.
Pendekatan mana yang lebih baik? Apa perbedaannya? Apa cara terbaik untuk memilih baris acak?
sql
performance
postgresql
random
nanounanue
sumber
sumber
Jawaban:
Dengan spesifikasi Anda (ditambah info tambahan di komentar),
Kueri di bawah ini tidak memerlukan pemindaian berurutan dari tabel besar, hanya pemindaian indeks.
Pertama, dapatkan taksiran untuk kueri utama:
Bagian hanya mungkin mahal adalah
count(*)
(untuk meja besar). Dengan spesifikasi di atas, Anda tidak memerlukannya. Perkiraan akan baik-baik saja, tersedia hampir tanpa biaya ( penjelasan terperinci di sini ):Selama
ct
tidak jauh lebih kecil dari ituid_span
, kueri akan mengungguli pendekatan lain.Hasilkan angka acak di
id
ruang. Anda memiliki "beberapa celah", jadi tambahkan 10% (cukup untuk menutupi dengan mudah) ke jumlah baris yang akan diambil.Masing
id
- masing dapat dipilih beberapa kali secara kebetulan (meskipun sangat tidak mungkin dengan ruang id besar), jadi kelompokkan angka yang dihasilkan (atau gunakanDISTINCT
).Bergabunglah dengan
id
s ke meja besar. Ini harus sangat cepat dengan indeks di tempat.Akhirnya pangkas kelebihan
id
yang belum dimakan oleh dupes dan celah. Setiap baris memiliki peluang yang sepenuhnya sama untuk dipetik.Versi pendek
Anda dapat menyederhanakan pertanyaan ini. CTE dalam kueri di atas hanya untuk tujuan pendidikan:
Saring dengan rCTE
Terutama jika Anda tidak begitu yakin tentang kesenjangan dan perkiraan.
Kami dapat bekerja dengan surplus yang lebih kecil dalam kueri basis. Jika ada terlalu banyak celah sehingga kami tidak menemukan cukup baris di iterasi pertama, rCTE terus beralih dengan istilah rekursif. Kita masih membutuhkan celah yang relatif sedikit di ruang ID atau rekursi bisa mengering sebelum batas tercapai - atau kita harus mulai dengan buffer yang cukup besar yang menentang tujuan mengoptimalkan kinerja.
Duplikat dihilangkan oleh
UNION
di rCTE.Bagian luar
LIMIT
membuat CTE berhenti segera setelah kami memiliki cukup baris.Permintaan ini dirancang dengan hati-hati untuk menggunakan indeks yang tersedia, menghasilkan baris yang benar-benar acak dan tidak berhenti sampai kami memenuhi batas (kecuali rekursi menjadi kering). Ada sejumlah jebakan di sini jika Anda ingin menulis ulang.
Bungkus ke dalam fungsi
Untuk penggunaan berulang dengan berbagai parameter:
Panggilan:
Anda bahkan dapat membuat generik ini berfungsi untuk tabel apa pun: Ambil nama kolom PK dan tabel sebagai tipe polimorfik dan gunakan
EXECUTE
... Tapi itu di luar cakupan pertanyaan ini. Lihat:Alternatif yang mungkin
JIKA persyaratan Anda memungkinkan set yang sama untuk panggilan berulang (dan kita berbicara tentang panggilan berulang), saya akan mempertimbangkan pandangan terwujud . Jalankan query di atas satu kali dan tulis hasilnya ke sebuah tabel. Pengguna mendapatkan pilihan acak semu dengan kecepatan tinggi. Segarkan pilihan acak Anda pada interval atau acara yang Anda pilih.
Postgres 9.5 memperkenalkan
TABLESAMPLE SYSTEM (n)
Dimana
n
persentasenya. Manual:Penekanan berani saya. Ini sangat cepat , tetapi hasilnya tidak sepenuhnya acak . Manual lagi:
Jumlah baris yang dikembalikan dapat sangat bervariasi. Sebagai contoh kami, untuk mendapatkan sekitar 1000 baris:
Terkait:
Atau instal modul tambahan tsm_system_rows untuk mendapatkan jumlah baris yang diminta secara tepat (jika ada cukup) dan memungkinkan sintaks yang lebih nyaman:
Lihat jawaban Evan untuk detailnya.
Tapi itu masih belum sepenuhnya acak.
sumber
JOIN bigtbl t
yang merupakan kependekan dariJOIN bigtbl AS t
.t
adalah alias tabel untukbigtbl
. Tujuannya adalah untuk mempersingkat sintaks tetapi tidak diperlukan dalam kasus khusus ini. Saya menyederhanakan kueri dalam jawaban saya dan menambahkan versi sederhana.Anda dapat memeriksa dan membandingkan rencana eksekusi keduanya dengan menggunakan
Tes cepat pada tabel besar 1 menunjukkan, bahwa yang
ORDER BY
pertama mengurutkan tabel lengkap dan kemudian mengambil 1000 item pertama. Mengurutkan tabel besar tidak hanya membaca tabel itu tetapi juga melibatkan membaca dan menulis file sementara. Thewhere random() < 0.1
hanya scan tabel lengkap sekali.Untuk tabel besar, ini mungkin bukan yang Anda inginkan karena bahkan satu pemindaian tabel lengkap mungkin butuh waktu lama.
Proposal ketiga adalah
Yang ini menghentikan pemindaian tabel segera setelah 1000 baris telah ditemukan dan karenanya kembali lebih cepat. Tentu saja ini sedikit mengurangi keacakannya, tetapi mungkin ini cukup baik untuk kasus Anda.
Sunting: Selain pertimbangan ini, Anda dapat memeriksa pertanyaan yang sudah diajukan untuk ini. Menggunakan kueri
[postgresql] random
menghasilkan beberapa klik.Dan artikel terkait depez yang menguraikan beberapa pendekatan lagi:
1 "besar" seperti pada "tabel lengkap tidak akan masuk ke dalam memori".
sumber
random() < 0.02
dan mengocok daftar itu, lalulimit 1000
! Pengurutan akan lebih murah pada beberapa ribu baris (lol).urutan postgresql secara acak (), pilih baris dalam urutan acak:
pesanan postgresql secara acak () dengan yang berbeda:
pesanan postgresql dengan batas acak satu baris:
sumber
select your_columns from your_table ORDER BY random() limit 1
mengambil ~ 2 menit untuk exec pada baris 45milDimulai dengan PostgreSQL 9.5, ada sintaks baru yang didedikasikan untuk mendapatkan elemen acak dari tabel:
Contoh ini akan memberi Anda 5% elemen dari
mytable
.Lihat penjelasan lebih lanjut di posting blog ini: http://www.postgresql.org/docs/current/static/sql-select.html
sumber
TABLESAMPLE SYSTEM_ROWS(400)
untuk mendapatkan sampel 400 baris acak. Anda perlu mengaktifkan built-intsm_system_rows
ekstensi untuk menggunakan pernyataan ini.Yang dengan ORDER BY akan menjadi yang lebih lambat.
select * from table where random() < 0.01;
pergi merekam dengan catatan, dan memutuskan untuk secara acak memfilternya atau tidak. Ini akan menjadiO(N)
karena hanya perlu memeriksa setiap catatan sekali.select * from table order by random() limit 1000;
akan mengurutkan seluruh tabel, lalu memilih 1000 yang pertama. Selain sihir voodoo di belakang layar, urutannya adalahO(N * log N)
.Kelemahan dari yang
random() < 0.01
satu adalah bahwa Anda akan mendapatkan sejumlah variabel catatan keluaran.Catatan, ada cara yang lebih baik untuk mengacak satu set data daripada mengurutkan secara acak: The Fisher-Yates Shuffle , yang berjalan di
O(N)
. Menerapkan shuffle dalam SQL sepertinya cukup menantang.sumber
Ini keputusan yang cocok untuk saya. Saya kira itu sangat sederhana untuk dipahami dan dieksekusi.
sumber
ORDER BY random()
yang berfungsi tetapi mungkin tidak efisien ketika bekerja dengan meja besar.Jika Anda tahu berapa banyak baris yang Anda inginkan, periksa
tsm_system_rows
.tsm_system_rows
Pertama instal ekstensi
Lalu pertanyaan Anda,
sumber
SYSTEM
metode bawaan.tsm_system_rows
dantsm_system_time
ekstensi. Sejauh yang saya bisa lihat, mereka sebenarnya tidak berguna untuk apa pun kecuali pemilihan baris acak yang minimal . Saya akan berterima kasih jika Anda dapat melihat dan mengomentari validitas atau analisis saya.Jika Anda ingin hanya satu baris, Anda dapat menggunakan
offset
turunan terhitungcount
.sumber
Variasi tampilan terwujud "Alternatif yang memungkinkan" yang diuraikan oleh Erwin Brandstetter dimungkinkan.
Katakan, misalnya, bahwa Anda tidak ingin duplikat dalam nilai acak yang dikembalikan. Jadi, Anda perlu menetapkan nilai boolean pada tabel utama yang berisi set nilai Anda (non-acak).
Dengan asumsi ini adalah tabel input:
Isi
ID_VALUES
tabel sesuai kebutuhan. Kemudian, seperti dijelaskan oleh Erwin, buat tampilan terwujud yang mengacakID_VALUES
tabel sekali:Perhatikan bahwa tampilan terwujud tidak mengandung kolom yang digunakan, karena ini akan dengan cepat menjadi usang. Tampilan juga tidak perlu mengandung kolom lain yang mungkin ada di
id_values
tabel.Untuk mendapatkan (dan "mengonsumsi") nilai acak, gunakan UPDATE-RETURNING on
id_values
, pilihid_values
dariid_values_randomized
dengan join, dan terapkan kriteria yang diinginkan untuk mendapatkan hanya kemungkinan yang relevan. Sebagai contoh:Ubah
LIMIT
seperlunya - jika Anda hanya perlu satu nilai acak pada satu waktu, ubahLIMIT
ke1
.Dengan indeks yang tepat aktif
id_values
, saya percaya UPDATE-RETURNING harus dijalankan dengan sangat cepat dengan sedikit beban. Ini mengembalikan nilai acak dengan satu perjalanan pulang pergi database. Kriteria untuk baris "yang memenuhi syarat" bisa serumit yang dipersyaratkan. Baris baru dapat ditambahkan keid_values
tabel kapan saja, dan mereka akan dapat diakses ke aplikasi segera setelah tampilan terwujud di-refresh (yang kemungkinan dapat dijalankan pada waktu yang tidak sibuk). Pembuatan dan penyegaran tampilan terwujud akan lambat, tetapi hanya perlu dijalankan ketika id baru ditambahkan keid_values
tabel.sumber
Satu pelajaran dari pengalaman saya:
offset floor(random() * N) limit 1
tidak lebih cepat dariorder by random() limit 1
.Saya pikir
offset
pendekatannya akan lebih cepat karena harus menghemat waktu penyortiran Postgres. Ternyata tidak.sumber
Tambahkan kolom yang disebut
r
dengan tipeserial
. Indeksr
.Asumsikan kita memiliki 200.000 baris, kita akan menghasilkan angka acak
n
, di mana 0 <n
<<= 200, 000.Pilih baris dengan
r > n
, urutkanASC
dan pilih yang terkecil.Kode:
Kode ini jelas. Subquery di tengah digunakan untuk dengan cepat memperkirakan jumlah baris tabel dari https://stackoverflow.com/a/7945274/1271094 .
Di level aplikasi Anda perlu menjalankan pernyataan lagi jika
n
> jumlah baris atau perlu memilih beberapa baris.sumber
Saya tahu saya sedikit terlambat ke pesta, tetapi saya baru saja menemukan alat yang luar biasa ini disebut pg_sample :
Saya mencoba ini dengan database 350M baris dan itu sangat cepat, tidak tahu tentang keacakan .
sumber