Saya memiliki tabel di postgres yang berisi beberapa juta baris. Saya telah memeriksanya di internet dan saya menemukan yang berikut ini
SELECT myid FROM mytable ORDER BY RANDOM() LIMIT 1;
Ini berfungsi, tetapi sangat lambat ... apakah ada cara lain untuk membuat kueri itu, atau cara langsung untuk memilih baris acak tanpa membaca semua tabel? Ngomong-ngomong, 'myid' adalah bilangan bulat tetapi dapat menjadi bidang kosong.
postgresql
random
Juan
sumber
sumber
Jawaban:
Anda mungkin ingin bereksperimen dengan
OFFSET
, seperti padaSELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
Ini
N
adalah jumlah baris dalammytable
. Anda mungkin perlu melakukan a terlebih dahuluSELECT COUNT(*)
untuk mengetahui nilaiN
.Pembaruan (oleh Antony Hatchkins)
Anda harus menggunakan di
floor
sini:SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
Pertimbangkan tabel 2 baris;
random()*N
menghasilkan0 <= x < 2
dan misalnyaSELECT myid FROM mytable OFFSET 1.7 LIMIT 1;
mengembalikan 0 baris karena pembulatan implisit ke int terdekat.sumber
SELECT COUNT(*)
?, maksud saya, tidak menggunakan semua nilai dalam tabel tetapi hanya sebagian?EXPLAIN SELECT ...
dengan nilai N yang berbeda memberikan biaya yang sama untuk kueri, maka saya kira lebih baik menggunakan nilai maksimum N.PostgreSQL 9.5 memperkenalkan pendekatan baru untuk pemilihan sampel yang jauh lebih cepat: TABLESAMPLE
Sintaksnya adalah
SELECT * FROM my_table TABLESAMPLE BERNOULLI(percentage); SELECT * FROM my_table TABLESAMPLE SYSTEM(percentage);
Ini bukan solusi optimal jika Anda hanya ingin satu baris dipilih, karena Anda perlu mengetahui JUMLAH tabel untuk menghitung persentase yang tepat.
Untuk menghindari HITUNGAN lambat dan menggunakan TABLESAMPLE cepat untuk tabel dari 1 baris hingga miliaran baris, Anda dapat melakukan:
SELECT * FROM my_table TABLESAMPLE SYSTEM(0.000001) LIMIT 1; -- if you got no result: SELECT * FROM my_table TABLESAMPLE SYSTEM(0.00001) LIMIT 1; -- if you got no result: SELECT * FROM my_table TABLESAMPLE SYSTEM(0.0001) LIMIT 1; -- if you got no result: SELECT * FROM my_table TABLESAMPLE SYSTEM(0.001) LIMIT 1; ...
Ini mungkin tidak terlihat begitu elegan, tetapi mungkin lebih cepat daripada jawaban lainnya.
Untuk memutuskan apakah Anda ingin menggunakan BERNULLI atau SYSTEM, baca perbedaannya di http://blog.2ndquadrant.com/tablesample-in-postgresql-9-5-2/
sumber
SELECT * FROM my_table TABLESAMPLE SYSTEM(SELECT 1/COUNT(*) FROM my_table) LIMIT 1;
?SELECT reltuples FROM pg_class WHERE relname = 'my_table'
untuk estimasi hitungan.Saya mencoba ini dengan subquery dan berhasil dengan baik. Offset, setidaknya di Postgresql v8.4.4 berfungsi dengan baik.
select * from mytable offset random() * (select count(*) from mytable) limit 1 ;
sumber
Anda perlu menggunakan
floor
:SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
sumber
random()*N
menghasilkan 0 <= x <2 dan misalnyaSELECT myid FROM mytable OFFSET 1.7 LIMIT 1;
mengembalikan 0 baris karena pembulatan implisit ke int terdekat.order by random()
, kira-kira3*O(N) < O(NlogN)
angka realife akan sedikit berbeda karena indeks.WHERE myid NOT IN (1st-myid)
danWHERE myid NOT IN (1st-myid, 2nd-myid)
tidak akan berfungsi karena keputusan dibuat oleh OFFSET. Hmmm ... Saya kira saya bisa mengurangi N sebesar 1 dan 2 di SELECT kedua dan ketiga.floor()
? Keuntungan apa yang ditawarkannya?Lihat tautan ini untuk beberapa opsi berbeda. http://www.depesz.com/index.php/2007/09/16/my-thoughts-on-getting-random-row/
Memperbarui: (A. Hatchkins)
Rangkuman artikel (sangat) panjang itu adalah sebagai berikut.
Penulis mendaftar empat pendekatan:
1)
ORDER BY random() LIMIT 1;
- lambat2)
ORDER BY id where id>=random()*N LIMIT 1
- tidak seragam jika ada celah3) kolom acak - perlu diperbarui sesekali
4) agregat acak khusus - metode licik, bisa lambat: random () perlu dibuat N kali
dan menyarankan untuk meningkatkan metode # 2 dengan menggunakan
5)
ORDER BY id where id=random()*N LIMIT 1
dengan permintaan berikutnya jika hasilnya kosong.sumber
Cara termudah dan tercepat untuk mengambil baris acak adalah dengan menggunakan
tsm_system_rows
ekstensi:CREATE EXTENSION IF NOT EXISTS tsm_system_rows;
Kemudian Anda dapat memilih jumlah baris yang Anda inginkan:
SELECT myid FROM mytable TABLESAMPLE SYSTEM_ROWS(1);
Ini tersedia dengan PostgreSQL 9.5 dan yang lebih baru.
Lihat: https://www.postgresql.org/docs/current/static/tsm-system-rows.html
sumber
ORDER BY random() LIMIT 1;
seharusnya cukup cepat.Saya telah menemukan solusi yang sangat cepat tanpa
TABLESAMPLE
. Jauh lebih cepat dariOFFSET random()*N LIMIT 1
. Itu bahkan tidak membutuhkan hitungan tabel.Idenya adalah membuat indeks ekspresi dengan data acak tetapi dapat diprediksi, misalnya
md5(primary key)
.Berikut adalah pengujian dengan sampel data 1 juta baris:
create table randtest (id serial primary key, data int not null); insert into randtest (data) select (random()*1000000)::int from generate_series(1,1000000); create index randtest_md5_id_idx on randtest (md5(id::text)); explain analyze select * from randtest where md5(id::text)>md5(random()::text) order by md5(id::text) limit 1;
Hasil:
Query ini terkadang (dengan probabilitas sekitar 1 / Number_of_rows) mengembalikan 0 baris, sehingga perlu diperiksa dan dijalankan ulang. Probabilitas juga tidak persis sama - beberapa baris lebih mungkin daripada yang lain.
Untuk perbandingan:
explain analyze SELECT id FROM randtest OFFSET random()*1000000 LIMIT 1;
Hasil sangat bervariasi, tetapi bisa sangat buruk:
sumber