Saya memiliki sqlite
tabel dengan skema berikut:
CREATE TABLE foo (bar VARCHAR)
Saya menggunakan tabel ini sebagai tempat penyimpanan daftar string.
Bagaimana cara memilih baris acak dari tabel ini?
Saya memiliki sqlite
tabel dengan skema berikut:
CREATE TABLE foo (bar VARCHAR)
Saya menggunakan tabel ini sebagai tempat penyimpanan daftar string.
Bagaimana cara memilih baris acak dari tabel ini?
Jawaban:
Lihat Memilih Baris Acak dari Tabel SQLite
SELECT * FROM table ORDER BY RANDOM() LIMIT 1;
SELECT a.foo FROM a JOIN b ON a.id = b.id WHERE b.bar = 2 ORDER BY RANDOM() LIMIT 1;
saya selalu mendapatkan baris yang sama.
Solusi berikut jauh lebih cepat daripada solusi anktastic (penghitungan (*) menghabiskan banyak biaya, tetapi jika Anda dapat menyimpannya dalam cache, maka perbedaannya tidak boleh sebesar itu), yang dengan sendirinya jauh lebih cepat daripada "pesan secara acak ()" bila Anda memiliki banyak baris, meskipun ada beberapa ketidaknyamanan.
Jika rowid Anda agak padat (mis. Beberapa penghapusan), maka Anda dapat melakukan hal berikut (menggunakan (select max(rowid) from foo)+1
alih-alih max(rowid)+1
memberikan kinerja yang lebih baik, seperti yang dijelaskan dalam komentar):
select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1));
Jika Anda memiliki lubang, Anda kadang-kadang akan mencoba untuk memilih rowid yang tidak ada, dan pemilihan tersebut akan mengembalikan kumpulan hasil kosong. Jika ini tidak dapat diterima, Anda dapat memberikan nilai default seperti ini:
select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1)) or rowid = (select max(rowid) from node) order by rowid limit 1;
Solusi kedua ini tidak sempurna: distribusi probabilitas lebih tinggi pada baris terakhir (yang memiliki rowid tertinggi), tetapi jika Anda sering menambahkan barang ke tabel, itu akan menjadi target bergerak dan distribusi probabilitas harus jauh lebih baik.
Namun solusi lain, jika Anda sering memilih barang acak dari tabel dengan banyak lubang, maka Anda mungkin ingin membuat tabel yang berisi baris tabel asli yang diurutkan secara acak:
create table random_foo(foo_id);
Kemudian secara berkala, isi kembali tabel random_foo
delete from random_foo;
insert into random_foo select id from foo;
Dan untuk memilih baris acak, Anda dapat menggunakan metode pertama saya (tidak ada lubang di sini). Tentu saja, metode terakhir ini memiliki beberapa masalah konkurensi, tetapi pembangunan kembali random_foo adalah operasi pemeliharaan yang tidak mungkin terjadi terlalu sering.
Namun, ada cara lain yang baru-baru ini saya temukan di milis , adalah dengan memasang pemicu pada delete untuk memindahkan baris dengan rowid terbesar ke baris yang saat ini dihapus, sehingga tidak ada lubang yang tersisa.
Terakhir, perhatikan bahwa perilaku rowid dan autoincrement kunci primer integer tidak identik (dengan rowid, ketika baris baru dimasukkan, maks (rowid) +1 dipilih, sedangkan nilai tertinggi-yang pernah dilihat + 1 untuk kunci utama), jadi solusi terakhir tidak akan berfungsi dengan peningkatan otomatis di random_foo, tetapi metode lain akan berfungsi.
SELECT max(rowid) + 1
akan menjadi kueri yang lambat - ini membutuhkan pemindaian tabel lengkap. sqlite hanya mengoptimalkan kueri SELECT max(rowid)
. Jadi, jawaban ini akan diperbaiki dengan: select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1));
Lihat ini untuk info lebih lanjut: sqlite.1065341.n5.nabble.com/…
Anda perlu menempatkan "order by ACAK ()" pada kueri Anda.
Contoh:
select * from quest order by RANDOM();
Mari kita lihat contoh lengkapnya
CREATE TABLE quest (
id INTEGER PRIMARY KEY AUTOINCREMENT,
quest TEXT NOT NULL,
resp_id INTEGER NOT NULL
);
Memasukkan beberapa nilai:
insert into quest(quest, resp_id) values ('1024/4',6), ('256/2',12), ('128/1',24);
Pilihan default:
select * from quest;
| id | quest | resp_id |
1 1024/4 6
2 256/2 12
3 128/1 24
--
Acak pilih:
select * from quest order by RANDOM();
| id | quest | resp_id |
3 128/1 24
1 1024/4 6
2 256/2 12
--
* Setiap kali Anda memilih, urutannya akan berbeda.
Jika Anda ingin mengembalikan hanya satu baris
select * from quest order by RANDOM() LIMIT 1;
| id | quest | resp_id |
2 256/2 12
--
* Setiap kali Anda memilih, hasil akan berbeda.
Bagaimana dengan:
SELECT COUNT(*) AS n FROM foo;
lalu pilih nomor acak m di [0, n) dan
SELECT * FROM foo LIMIT 1 OFFSET m;
Anda bahkan dapat menyimpan angka pertama ( n ) di suatu tempat dan hanya memperbaruinya ketika jumlah database berubah. Dengan begitu Anda tidak perlu melakukan PILIHAN MENGHITUNG setiap waktu.
OFFSET
tampaknya naik tergantung pada ukuran offset - baris 2 cepat, baris 2 juta membutuhkan waktu, bahkan ketika semua data dalam ukuran tetap dan itu harus bisa mencari langsung ke sana. Setidaknya, seperti itulah tampilannya di SQLite 3.7.13.
SELECT bar
FROM foo
ORDER BY Random()
LIMIT 1
Berikut modifikasi solusi @ ank:
SELECT *
FROM table
LIMIT 1
OFFSET ABS(RANDOM()) % MAX((SELECT COUNT(*) FROM table), 1)
Solusi ini juga berfungsi untuk indeks dengan celah, karena kami mengacak offset dalam rentang [0, hitung). MAX
digunakan untuk menangani kasus dengan tabel kosong.
Berikut hasil tes sederhana pada tabel dengan 16k baris:
sqlite> .timer on
sqlite> select count(*) from payment;
16049
Run Time: real 0.000 user 0.000140 sys 0.000117
sqlite> select payment_id from payment limit 1 offset abs(random()) % (select count(*) from payment);
14746
Run Time: real 0.002 user 0.000899 sys 0.000132
sqlite> select payment_id from payment limit 1 offset abs(random()) % (select count(*) from payment);
12486
Run Time: real 0.001 user 0.000952 sys 0.000103
sqlite> select payment_id from payment order by random() limit 1;
3134
Run Time: real 0.015 user 0.014022 sys 0.000309
sqlite> select payment_id from payment order by random() limit 1;
9407
Run Time: real 0.018 user 0.013757 sys 0.000208
Saya datang dengan solusi berikut untuk database sqlite3 yang besar :
SELECT * FROM foo WHERE rowid = abs(random()) % (SELECT max(rowid) FROM foo) + 1;
Fungsi abs (X) mengembalikan nilai absolut dari argumen numerik X.
Fungsi random () mengembalikan bilangan bulat pseudo-random antara -9223372036854775808 dan +9223372036854775807.
Operator% mengeluarkan nilai integer dari operan kirinya modulo operan kanannya.
Terakhir, Anda menambahkan +1 untuk mencegah rowid sama dengan 0.