Bagaimana cara memperluas solusi ini untuk bergabung? Saat menggunakan 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.
Helmut Grohne
Apakah mungkin untuk menyemai nomor acak. mis. Book of the day diunggulkan dengan unix epoc untuk hari ini pada siang hari sehingga menampilkan buku yang sama sepanjang hari meskipun kueri dijalankan beberapa kali. Ya, saya tahu caching lebih efisien untuk kasus penggunaan ini hanya sebagai contoh.
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)+1alih-alih max(rowid)+1memberikan 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:
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:
createtable random_foo(foo_id);
Kemudian secara berkala, isi kembali tabel random_foo
deletefrom random_foo;insertinto 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.
Seperti yang baru saja saya lihat di milis, alih-alih memiliki metode fallback (metode 2), Anda bisa menggunakan rowid> = [random] daripada =, tetapi sebenarnya sangat lambat dibandingkan dengan metode 2.
Suzanne Dupéron
3
Ini adalah jawaban yang bagus; bagaimanapun itu memiliki satu masalah. SELECT max(rowid) + 1akan 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/…
dasl
19
Anda perlu menempatkan "order by ACAK ()" pada kueri Anda.
Contoh:
select*from quest orderby RANDOM();
Mari kita lihat contoh lengkapnya
Buat tabel:
CREATETABLE quest (
id INTEGER PRIMARYKEY AUTOINCREMENT,
quest TEXT NOTNULL,
resp_id INTEGER NOTNULL);
Meskipun jawaban hanya kode tidak dilarang, harap dipahami bahwa ini adalah komunitas Tanya Jawab, bukan komunitas sumber, dan biasanya, jika OP memahami kode yang diposting sebagai jawaban, dia akan muncul. dengan solusi serupa sendiri, dan tidak akan memposting pertanyaan sejak awal. Karena itu, berikan konteks pada jawaban dan / atau kode Anda dengan menjelaskan bagaimana dan / atau mengapa itu berhasil.
XenoRo
2
Saya lebih suka solusi ini, karena memungkinkan saya untuk mencari n baris. Dalam kasus saya, saya membutuhkan 100 sampel acak dari database - ORDER BY RANDOM () dikombinasikan dengan LIMIT 100 melakukan hal itu.
mnr
17
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.
Itu metode cepat yang bagus. Ini tidak menggeneralisasi dengan baik untuk memilih lebih dari 1 baris, tetapi OP hanya meminta 1, jadi saya rasa tidak apa-apa.
Ken Williams
Hal yang menarik untuk diperhatikan adalah bahwa waktu yang diperlukan untuk menemukan OFFSETtampaknya 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.
Ken Williams
@KenWiams Hampir semua database memiliki masalah yang sama dengan OFFSET. Ini adalah cara yang sangat tidak efisien untuk mengkueri database karena ia perlu membaca banyak baris meskipun hanya akan mengembalikan 1.
Jonathan Allen
1
Perhatikan bahwa saya berbicara tentang / ukuran tetap / catatan meskipun - seharusnya mudah untuk memindai langsung ke byte yang benar dalam data ( tidak membaca banyak baris), tetapi mereka harus menerapkan pengoptimalan secara eksplisit.
Ken Williams
@KenWiams: tidak ada record berukuran tetap di SQLite, ia diketik secara dinamis dan datanya tidak harus cocok dengan afinitas yang dideklarasikan ( sqlite.org/fileformat2.html#section_2_1 ). Semuanya disimpan di halaman b-tree, jadi bagaimanapun caranya setidaknya harus melakukan pencarian b-tree ke arah daun. Untuk mencapai hal ini secara efisien, perlu menyimpan ukuran subpohon bersama dengan setiap penunjuk anak. Ini akan menjadi overhead yang terlalu banyak untuk sedikit manfaat, karena Anda masih tidak akan dapat mengoptimalkan OFFSET untuk gabungan, diurutkan berdasarkan, dll ... (dan tanpa ORDER BY pesanan tidak ditentukan.)
Solusi ini juga berfungsi untuk indeks dengan celah, karena kami mengacak offset dalam rentang [0, hitung). MAXdigunakan 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.000user0.000140 sys 0.000117
sqlite>select payment_id from payment limit 1 offset abs(random())%(select count(*)from payment);14746
Run Time: real 0.002user0.000899 sys 0.000132
sqlite>select payment_id from payment limit 1 offset abs(random())%(select count(*)from payment);12486
Run Time: real 0.001user0.000952 sys 0.000103
sqlite>select payment_id from payment orderby random() limit 1;3134
Run Time: real 0.015user0.014022 sys 0.000309
sqlite>select payment_id from payment orderby random() limit 1;9407
Run Time: real 0.018user0.013757 sys 0.000208
Usaha yang bagus tapi saya rasa ini tidak akan berhasil. Bagaimana jika baris dengan rowId = 5 dihapus, tetapi rowIds 1,2,3,4,6,7,8,9,10 masih ada? Kemudian, jika rowId acak yang dipilih adalah 5, kueri ini tidak akan menghasilkan apa pun.
Jawaban:
Lihat Memilih Baris Acak dari Tabel SQLite
sumber
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-alihmax(rowid)+1
memberikan kinerja yang lebih baik, seperti yang dijelaskan dalam komentar):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:
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:
Kemudian secara berkala, isi kembali tabel random_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.
sumber
SELECT max(rowid) + 1
akan menjadi kueri yang lambat - ini membutuhkan pemindaian tabel lengkap. sqlite hanya mengoptimalkan kueriSELECT 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:
Mari kita lihat contoh lengkapnya
Memasukkan beberapa nilai:
Pilihan default:
Acak pilih:
* Setiap kali Anda memilih, urutannya akan berbeda.Jika Anda ingin mengembalikan hanya satu baris
* Setiap kali Anda memilih, hasil akan berbeda.sumber
Bagaimana dengan:
lalu pilih nomor acak m di [0, n) dan
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.
sumber
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.sumber
Berikut modifikasi solusi @ ank:
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:
sumber
Saya datang dengan solusi berikut untuk database sqlite3 yang besar :
Terakhir, Anda menambahkan +1 untuk mencegah rowid sama dengan 0.
sumber