Ya, jika Anda memiliki celah besar dalam ID maka kemungkinan ID terendah Anda diambil secara acak jauh lebih rendah daripada ID tinggi Anda. Faktanya kesempatan bahwa ID pertama setelah jeda terbesar diambil sebenarnya yang tertinggi. Oleh karena itu ini tidak acak menurut definisi.
lukeocodes
6
Bagaimana Anda mendapatkan 10 baris acak berbeda? Apakah Anda harus menetapkan batas ke 10 dan kemudian mengulanginya 10 kali dengan mysqli_fetch_assoc($result)? Atau apakah 10 hasil itu belum tentu dapat dibedakan?
Adam
12
Acak membutuhkan peluang yang sama untuk hasil apa pun, dalam pikiranku. ;)
lukeocodes
4
Artikel lengkap membahas masalah-masalah seperti distribusi yang tidak merata dan hasil yang berulang.
Bradd Szonye
1
khususnya, jika Anda memiliki celah di awal ID Anda, yang pertama akan dipilih (min / maks-min) saat itu. Untuk kasus itu, tweak sederhana adalah MAX () - MIN () * RAND + MIN (), yang tidak terlalu lambat.
Mateusz - proof pls, SELECT words, transcription, translation, sound FROM vocabulary WHERE menu_id=$menuId ORDER BY RAND() LIMIT 10butuh 0,0010, tanpa LIMIT 10 butuh 0,0012 (dalam tabel itu 3500 kata).
Arthur Kushman
26
@zeusakm 3500 kata tidak banyak; masalahnya adalah ia meledak melewati titik tertentu karena MySQL harus benar-benar mengurutkan SEMUA catatan setelah membaca masing-masing; sekali operasi itu menyentuh hard disk Anda dapat merasakan perbedaannya.
Ja͢ck
16
Saya tidak ingin mengulang sendiri tetapi sekali lagi, itu adalah pemindaian tabel penuh. Di meja besar itu sangat memakan waktu dan memori dan dapat menyebabkan pembuatan & operasi di atas meja sementara pada disk yang sangat lambat.
matt
10
Ketika saya mewawancarai Facebook pada tahun 2010, mereka bertanya kepada saya bagaimana memilih catatan acak dari file besar dengan ukuran yang tidak diketahui, dalam satu bacaan. Setelah Anda menemukan sebuah ide, mudah untuk menggeneralisasikannya untuk memilih beberapa catatan. Jadi ya, menyortir seluruh file itu konyol. Pada saat yang sama, ini sangat berguna. Saya hanya menggunakan pendekatan ini untuk memilih 10 baris acak dari sebuah tabel dengan 1.000.000 + baris. Tentu, saya harus menunggu sedikit; tapi saya hanya ingin mendapatkan ide, seperti apa bentuk baris dalam tabel ini ...
osa
27
Permintaan sederhana yang memiliki kinerja luar biasa dan bekerja dengan kesenjangan :
SELECT*FROM tbl AS t1 JOIN(SELECT id FROM tbl ORDERBY RAND() LIMIT 10)as t2 ON t1.id=t2.id
Kueri ini pada tabel 200K memakan waktu 0,08s dan versi normal (PILIH * DARI ORDER tbl DENGAN RAND () LIMIT 10) membutuhkan 0,35s pada mesin saya.
Ini cepat karena fase pengurutan hanya menggunakan kolom ID yang diindeks. Anda dapat melihat perilaku ini di penjelasan:
PILIH * DARI Tbl ORDER OLEH RAND () LIMIT 10:
SELECT * DARI tbl AS t1 BERGABUNG (SELECT id FROM tbl ORDER BY RAND () LIMIT 10) sebagai t2 ON t1.id = t2.id
Maaf, saya diuji! kinerja lambat pada catatan 600 ribu.
Dylan B
@DylanB Saya memperbarui jawabannya dengan ujian.
Ali
17
Saya mendapatkan pertanyaan cepat (sekitar 0,5 detik) dengan cpu lambat , memilih 10 baris acak dalam 400K register database MySQL ukuran 2Gb non-cache. Lihat di sini kode saya: Pilihan cepat baris acak di MySQL
<?php
$time= microtime_float();$sql='SELECT COUNT(*) FROM pages';$rquery= BD_Ejecutar($sql);
list($num_records)=mysql_fetch_row($rquery);
mysql_free_result($rquery);$sql="SELECT id FROM pages WHERE RAND()*$num_records<20
ORDER BY RAND() LIMIT 0,10";$rquery= BD_Ejecutar($sql);while(list($id)=mysql_fetch_row($rquery)){if($id_in)$id_in.=",$id";else$id_in="$id";}
mysql_free_result($rquery);$sql="SELECT id,url FROM pages WHERE id IN($id_in)";$rquery= BD_Ejecutar($sql);while(list($id,$url)=mysql_fetch_row($rquery)){
logger("$id, $url",1);}
mysql_free_result($rquery);$time= microtime_float()-$time;
logger("num_records=$num_records",1);
logger("$id_in",1);
logger("Time elapsed: <b>$time segundos</b>",1);?>
Dengan catatan saya lebih dari 14 juta tabel, ini sangat lambatORDER BY RAND()
Fabrizio
5
@snippetsofcode Dalam kasus Anda - 400k baris Anda dapat menggunakan "ORDER BY rand ()" sederhana. Trik Anda dengan 3 pertanyaan tidak berguna. Anda dapat menulis ulang seperti "SELECT id, url DARI halaman WHERE id IN (SELECT id DARI halaman ORDER BY rand () LIMIT 10)"
Roman Podlinov
4
Teknik Anda masih melakukan pemindaian tabel. Gunakan FLUSH STATUS; SELECT ...; SHOW SESSION STATUS LIKE 'Handler%';untuk melihatnya.
Rick James
4
Coba juga jalankan kueri itu di halaman web 200 req / s. Concurrency akan membunuhmu.
Marki555
@RomanPodlinov manfaat dari ini di atas dataran ORDER BY RAND()adalah bahwa ia hanya mengurutkan id (bukan baris penuh), sehingga tabel temp lebih kecil, tetapi masih harus mengurutkan semuanya.
Marki555
16
Permintaan baris yang sangat sederhana dan tunggal.
Terkadang SLOW diterima jika saya ingin tetap SEDERHANA
Pengindeksan harus diterapkan di atas meja jika besar.
Muhammad Azeem
1
Pengindeksan tidak akan membantu di sini. Indeks sangat membantu untuk hal-hal yang sangat spesifik, dan kueri ini bukan salah satunya.
Andrew
13
Dari buku:
Pilih Baris Acak Menggunakan Offset
Masih teknik lain yang menghindari masalah yang ditemukan dalam alternatif sebelumnya adalah menghitung baris dalam kumpulan data dan mengembalikan angka acak antara 0 dan hitungan. Kemudian gunakan nomor ini sebagai offset saat menanyakan kumpulan data
Gunakan solusi ini ketika Anda tidak dapat mengasumsikan nilai kunci yang berdekatan dan Anda perlu memastikan setiap baris memiliki peluang yang sama untuk dipilih.
Itu membantu beberapa untuk MyISAM, tetapi tidak untuk InnoDB (dengan asumsi id adalah clustered PRIMARY KEY).
Rick James
7
Nah, jika Anda tidak memiliki celah pada kunci Anda dan semuanya berupa angka, Anda dapat menghitung angka acak dan memilih garis itu. tetapi ini mungkin tidak akan terjadi.
yang pada dasarnya akan memastikan bahwa Anda mendapatkan nomor acak dalam kisaran kunci Anda dan kemudian Anda memilih yang terbaik berikutnya yang lebih besar. Anda harus melakukan ini 10 kali.
namun ini TIDAK benar-benar acak karena kunci Anda kemungkinan besar tidak akan didistribusikan secara merata.
Ini benar-benar masalah besar dan tidak mudah untuk menyelesaikan semua persyaratan, rand MySQL () adalah yang terbaik yang bisa Anda dapatkan jika Anda benar-benar menginginkan 10 baris acak.
Pertanyaannya adalah seberapa acak Anda membutuhkannya?
Bisakah Anda menjelaskan lebih banyak sehingga saya bisa memberikan solusi yang baik.
Sebagai contoh, sebuah perusahaan tempat saya bekerja memiliki solusi di mana mereka membutuhkan keacakan mutlak sangat cepat. Mereka berakhir dengan pra-mengisi database dengan nilai acak yang dipilih turun dan diatur ke nilai acak yang berbeda setelah itu lagi.
Jika Anda hampir tidak pernah memperbarui Anda juga bisa mengisi id tambahan sehingga Anda tidak memiliki celah dan hanya dapat menghitung kunci acak sebelum memilih ... Itu tergantung pada kasus penggunaan!
Hai Joe. Dalam kasus khusus ini kunci tidak boleh kekurangan celah, tetapi seiring waktu hal ini dapat berubah. Dan sementara jawaban Anda bekerja, itu akan menghasilkan 10 baris acak (asalkan saya menulis batas 10) yang berurutan dan saya ingin lebih banyak keacakan sehingga untuk berbicara. :) Terima kasih.
Francisc
Jika Anda perlu 10 gunakan semacam persatuan untuk menghasilkan 10 baris unik.
johno
Apa yang saya katakan. Anda perlu menjalankan itu 10 kali. menggabungkannya dengan serikat pekerja adalah salah satu cara untuk memasukkannya ke dalam satu permintaan. lihat addendum saya 2 menit yang lalu.
The Surrican
1
@TheSurrican, Solusi ini terlihat keren tetapi sangat cacat . Coba masukkan satu saja yang sangat besar Iddan semua kueri acak Anda akan mengembalikan yang itu Id.
Pacerier
1
FLOOR(RAND()*MAX(id))bias terhadap pengembalian id yang lebih besar.
Rick James
3
Saya membutuhkan kueri untuk mengembalikan sejumlah besar baris acak dari tabel yang agak besar. Inilah yang saya pikirkan. Pertama-tama dapatkan id rekaman maksimum:
SELECT MAX(id)FROM table_name;
Kemudian gantilah nilai itu menjadi:
SELECT*FROM table_name WHERE id > FLOOR(RAND()* max) LIMIT n;
Di mana max adalah id rekaman maksimum dalam tabel dan n adalah jumlah baris yang Anda inginkan di set hasil Anda. Asumsinya adalah bahwa tidak ada celah dalam id rekaman meskipun saya ragu itu akan mempengaruhi hasilnya jika ada (belum mencobanya). Saya juga membuat prosedur tersimpan ini menjadi lebih umum; masukkan nama tabel dan jumlah baris yang akan dikembalikan. Saya menjalankan MySQL 5.5.38 pada Windows 2008, 32GB, dual 3GHz E5450, dan di atas meja dengan 17.361.264 baris cukup konsisten pada ~ .03 detik / ~ 11 detik untuk mengembalikan 1.000.000 baris. (kali dari MySQL Workbench 6.1; Anda juga bisa menggunakan CEIL alih-alih LANTAI dalam pernyataan pilih kedua tergantung pada preferensi Anda)
Saya ingin menunjukkan kemungkinan percepatan lain - caching . Pikirkan mengapa Anda perlu mendapatkan baris acak. Mungkin Anda ingin menampilkan beberapa posting acak atau iklan acak di situs web. Jika Anda mendapatkan 100 req / s, apakah benar-benar diperlukan setiap pengunjung mendapatkan baris acak? Biasanya baik-baik saja untuk men-cache X ini baris acak selama 1 detik (atau bahkan 10 detik) Tidak masalah jika 100 pengunjung unik dalam 1 detik yang sama mendapatkan posting acak yang sama, karena detik berikutnya 100 pengunjung lainnya akan mendapatkan serangkaian posting yang berbeda.
Saat menggunakan caching ini, Anda juga dapat menggunakan beberapa solusi yang lebih lambat untuk mendapatkan data acak karena akan diambil dari MySQL hanya sekali per detik terlepas dari kebutuhan Anda.
Saya memperbaiki jawaban yang dimiliki @Riedsio. Ini adalah kueri paling efisien yang dapat saya temukan pada tabel besar, terdistribusi secara merata dengan celah (diuji untuk mendapatkan 1000 baris acak dari tabel yang memiliki> baris 2.6B).
(SELECT id FROMtableINNERJOIN(SELECT FLOOR(RAND()*@max :=(SELECT MAX(id)FROMtable))+1as rand) r on id > rand LIMIT 1)UNION(SELECT id FROMtableINNERJOIN(SELECT FLOOR(RAND()*@max)+1as rand) r on id > rand LIMIT 1)UNION(SELECT id FROMtableINNERJOIN(SELECT FLOOR(RAND()*@max)+1as rand) r on id > rand LIMIT 1)UNION(SELECT id FROMtableINNERJOIN(SELECT FLOOR(RAND()*@max)+1as rand) r on id > rand LIMIT 1)UNION(SELECT id FROMtableINNERJOIN(SELECT FLOOR(RAND()*@max)+1as rand) r on id > rand LIMIT 1)UNION(SELECT id FROMtableINNERJOIN(SELECT FLOOR(RAND()*@max)+1as rand) r on id > rand LIMIT 1)UNION(SELECT id FROMtableINNERJOIN(SELECT FLOOR(RAND()*@max)+1as rand) r on id > rand LIMIT 1)UNION(SELECT id FROMtableINNERJOIN(SELECT FLOOR(RAND()*@max)+1as rand) r on id > rand LIMIT 1)UNION(SELECT id FROMtableINNERJOIN(SELECT FLOOR(RAND()*@max)+1as rand) r on id > rand LIMIT 1)UNION(SELECT id FROMtableINNERJOIN(SELECT FLOOR(RAND()*@max)+1as rand) r on id > rand LIMIT 1)
Biarkan saya membongkar apa yang terjadi.
@max := (SELECT MAX(id) FROM table)
Saya menghitung dan menyimpan maks. Untuk tabel yang sangat besar, ada sedikit overhead untuk menghitung MAX(id)setiap kali Anda membutuhkan satu baris
SELECT FLOOR(rand() * @max) + 1 as rand)
Mendapat id acak
SELECT id FROM table INNER JOIN (...) on id > rand LIMIT 1
Ini mengisi celah. Pada dasarnya jika Anda secara acak memilih nomor di celah, itu hanya akan memilih id berikutnya. Dengan asumsi kesenjangan tersebar secara seragam, ini seharusnya tidak menjadi masalah.
Melakukan penyatuan membantu Anda memasukkan semuanya ke dalam 1 kueri sehingga Anda dapat menghindari melakukan beberapa kueri. Ini juga memungkinkan Anda menghemat biaya perhitungan MAX(id). Tergantung pada aplikasi Anda, ini mungkin penting atau sangat sedikit.
Perhatikan bahwa ini hanya mendapatkan id dan membuatnya secara acak. Jika Anda ingin melakukan sesuatu yang lebih maju, saya sarankan Anda melakukan ini:
SELECT t.id, t.name -- etc, etcFROMtable t
INNERJOIN((SELECT id FROMtableINNERJOIN(SELECT FLOOR(RAND()*@max :=(SELECT MAX(id)FROMtable))+1as rand) r on id > rand LIMIT 1)UNION(SELECT id FROMtableINNERJOIN(SELECT FLOOR(RAND()*@max)+1as rand) r on id > rand LIMIT 1)UNION(SELECT id FROMtableINNERJOIN(SELECT FLOOR(RAND()*@max)+1as rand) r on id > rand LIMIT 1)UNION(SELECT id FROMtableINNERJOIN(SELECT FLOOR(RAND()*@max)+1as rand) r on id > rand LIMIT 1)UNION(SELECT id FROMtableINNERJOIN(SELECT FLOOR(RAND()*@max)+1as rand) r on id > rand LIMIT 1)UNION(SELECT id FROMtableINNERJOIN(SELECT FLOOR(RAND()*@max)+1as rand) r on id > rand LIMIT 1)UNION(SELECT id FROMtableINNERJOIN(SELECT FLOOR(RAND()*@max)+1as rand) r on id > rand LIMIT 1)UNION(SELECT id FROMtableINNERJOIN(SELECT FLOOR(RAND()*@max)+1as rand) r on id > rand LIMIT 1)UNION(SELECT id FROMtableINNERJOIN(SELECT FLOOR(RAND()*@max)+1as rand) r on id > rand LIMIT 1)UNION(SELECT id FROMtableINNERJOIN(SELECT FLOOR(RAND()*@max)+1as rand) r on id > rand LIMIT 1)) x ON x.id = t.id
ORDERBY t.id
Saya memerlukan 30 catatan acak, jadi saya harus mengubah LIMIT 1ke LIMIT 30mana - mana dalam permintaan
Hassaan
@Hassaan Anda tidak boleh, bahwa mengubah LIMIT 1ke LIMIT 30akan membuat Anda 30 catatan berturut-turut dari titik acak di tabel. Anda seharusnya memiliki 30 salinan (SELECT id FROM ....bagian di tengah.
Hans Z
Saya sudah mencoba tetapi tampaknya tidak lebih efisien daripada Riedsiomenjawab. Saya telah mencoba dengan 500 hit per halaman ke halaman menggunakan PHP 7.0.22 dan MariaDB pada centos 7, dengan Riedsiojawaban saya mendapat 500+ respon ekstra sukses maka jawaban Anda.
Hassaan
1
@Hassaan jawaban riedsio memberikan 1 baris, yang ini memberi Anda n baris, serta menghemat overhead I / O untuk query. Anda mungkin bisa mendapatkan baris lebih cepat, tetapi dengan lebih banyak memuat pada sistem Anda.
DROP TEMPORARY TABLEIFEXISTS rands;CREATE TEMPORARY TABLE rands ( rand_id INT );
loop_me: LOOP
IF cnt <1THEN
LEAVE loop_me;ENDIF;INSERTINTO rands
SELECT r1.id
FROM random AS r1 JOIN(SELECT(RAND()*(SELECT MAX(id)FROM random))AS id)AS r2
WHERE r1.id >= r2.id
ORDERBY r1.id ASC
LIMIT 1;SET cnt = cnt -1;END LOOP loop_me;
Dalam artikel tersebut ia memecahkan masalah kesenjangan dalam id yang menyebabkan hasil tidak begitu acak dengan mempertahankan tabel (menggunakan pemicu, dll ... lihat artikel); Saya memecahkan masalah dengan menambahkan kolom lain ke tabel, diisi dengan angka yang berdekatan, mulai dari 1 ( edit: kolom ini ditambahkan ke tabel sementara yang dibuat oleh subquery saat runtime, tidak mempengaruhi tabel permanen Anda):
DROP TEMPORARY TABLEIFEXISTS rands;CREATE TEMPORARY TABLE rands ( rand_id INT );
loop_me: LOOP
IF cnt <1THEN
LEAVE loop_me;ENDIF;SET@no_gaps_id :=0;INSERTINTO rands
SELECT r1.id
FROM(SELECT id,@no_gaps_id :=@no_gaps_id +1AS no_gaps_id FROM random)AS r1 JOIN(SELECT(RAND()*(SELECT COUNT(*)FROM random))AS id)AS r2
WHERE r1.no_gaps_id >= r2.id
ORDERBY r1.no_gaps_id ASC
LIMIT 1;SET cnt = cnt -1;END LOOP loop_me;
Dalam artikel itu saya bisa melihat dia berusaha keras untuk mengoptimalkan kode; saya tidak tahu jika / seberapa besar perubahan saya berdampak pada kinerja tetapi bekerja sangat baik untuk saya.
"Saya tidak punya ide jika / seberapa besar perubahan saya berdampak pada kinerja" - cukup banyak. Untuk @no_gaps_idindeks tidak dapat digunakan, jadi jika Anda melihat EXPLAINpermintaan Anda, Anda memiliki Using filesortdan Using where(tanpa indeks) untuk subqueries, berbeda dengan permintaan asli.
Fabian Schmengler
2
Berikut adalah pengubah permainan yang mungkin bermanfaat bagi banyak orang;
Saya memiliki tabel dengan 200k baris, dengan id berurutan , saya harus memilih N baris acak, jadi saya memilih untuk menghasilkan nilai acak berdasarkan ID terbesar dalam tabel, saya membuat skrip ini untuk mencari tahu mana yang merupakan operasi tercepat:
logTime();
query("SELECT COUNT(id) FROM tbl");
logTime();
query("SELECT MAX(id) FROM tbl");
logTime();
query("SELECT id FROM tbl ORDER BY id DESC LIMIT 1");
logTime();
Hasilnya adalah:
Hitung: 36.8418693542479ms
Maks: 0.241041183472ms
Pesan: 0.216960906982ms
Berdasarkan hasil ini, order desc adalah operasi tercepat untuk mendapatkan max id,
Ini jawaban saya untuk pertanyaan:
SELECT GROUP_CONCAT(n SEPARATOR ',') g FROM(SELECT FLOOR(RAND()*(SELECT id FROM tbl ORDERBY id DESC LIMIT 1)) n FROM tbl LIMIT 10) a
...SELECT*FROM tbl WHERE id IN($result);
FYI: Untuk mendapatkan 10 baris acak dari tabel 200k, butuh 1,78 ms (termasuk semua operasi di sisi php)
Saya sedang memikirkan solusi yang sama, tolong beritahu saya, apakah lebih cepat daripada metode yang lain?
G. Adnane
@ G.Adnane tidak lebih cepat atau lebih lambat dari jawaban yang diterima, tetapi jawaban yang diterima mengasumsikan distribusi id yang sama. Saya tidak bisa membayangkan skenario di mana ini bisa dijamin. Solusi ini ada di O (1) di mana solusinya SELECT column FROM table ORDER BY RAND() LIMIT 10ada di O (nlog (n)). Jadi ya, ini adalah solusi puasa dan berfungsi untuk setiap distribusi id.
Adam
tidak, karena di tautan yang diposting untuk solusi yang diterima, ada metode lain, saya ingin tahu apakah solusi ini lebih cepat daripada yang lain, dengan cara lain, kita dapat mencoba mencari yang lain, itu sebabnya saya bertanya, dengan cara apa pun, +1 untuk jawabanmu. Saya menggunakan samething
G. Adnane
ada kasus ketika Anda ingin mendapatkan x jumlah baris tetapi offset menuju akhir tabel yang akan mengembalikan <x baris atau hanya 1 baris. saya tidak melihat jawaban Anda sebelum saya memposting milik saya tetapi saya membuatnya lebih jelas di sini stackoverflow.com/a/59981772/10387008
ZOLDIK
@ZOLDIK tampaknya Anda memilih 10 baris pertama setelah diimbangi x. Saya berpendapat bahwa ini bukan generasi acak 10 baris. Dalam jawaban saya, Anda harus menjalankan kueri di langkah tiga 10 kali, yaitu satu hanya mendapat satu baris per eksekusi dan tidak perlu khawatir jika offset ada di akhir tabel.
Adam
1
Jika Anda hanya memiliki satu Permintaan Baca
Gabungkan jawaban @redsio dengan temp-table (600K tidak terlalu banyak):
DROP TEMPORARY TABLEIFEXISTS tmp_randorder;CREATETABLE tmp_randorder (id int(11)notnull auto_increment primarykey, data_id int(11));INSERTINTO tmp_randorder (data_id)select id from datatable;
Dan kemudian ambil versi @redsios Jawaban:
SELECT dt.*FROM(SELECT(RAND()*(SELECT MAX(id)FROM tmp_randorder))AS id)AS rnd
INNERJOIN tmp_randorder rndo on rndo.id between rnd.id -10and rnd.id +10INNERJOIN datatable AS dt on dt.id = rndo.data_id
ORDERBY abs(rndo.id - rnd.id)
LIMIT 1;
Jika meja besar, Anda dapat mengayak pada bagian pertama:
INSERTINTO tmp_randorder (data_id)select id from datatable where rand()<0.01;
Jika Anda memiliki banyak permintaan baca
Versi: Anda bisa menyimpan tabel tmp_randorder tetap ada, sebut saja datatable_idlist. Buat ulang tabel itu dalam interval tertentu (hari, jam), karena meja juga akan berlubang. Jika meja Anda menjadi sangat besar, Anda juga bisa mengisi ulang lubang
pilih l.data_id secara keseluruhan dari datatable_idlist l kiri gabung datatable dt di dt.id = l.data_id di mana dt.id bernilai null;
Versi: Berikan Dataset Anda sebuah kolom random_sortorder baik secara langsung di datatable atau dalam tabel ekstra persisten datatable_sortorder. Buat indeks kolom itu. Hasilkan Nilai Acak di Aplikasi Anda (saya akan menyebutnya $rand).
select l.*from datatable l
orderby abs(random_sortorder -$rand)desc
limit 1;
Solusi ini membedakan 'baris tepi' dengan urutan random_sort tertinggi dan terendah, jadi atur ulangnya dalam interval (sekali sehari).
Solusi sederhana lain adalah memberi peringkat pada baris dan mengambil salah satunya secara acak dan dengan solusi ini Anda tidak perlu memiliki kolom berdasarkan 'Id' di tabel.
SELECT d.*FROM(SELECT t.*,@rownum :=@rownum +1AS rank
FROM mytable AS t,(SELECT@rownum :=0)AS r,(SELECT@cnt :=(SELECT RAND()*(SELECT COUNT(*)FROM mytable)))AS n
) d WHERE rank >=@cnt LIMIT 10;
Anda dapat mengubah nilai batas sesuai kebutuhan Anda untuk mengakses baris sebanyak yang Anda inginkan tetapi itu sebagian besar akan menjadi nilai berturut-turut.
Namun, jika Anda tidak ingin nilai acak berturut-turut maka Anda dapat mengambil sampel yang lebih besar dan memilih secara acak dari itu. sesuatu seperti ...
SELECT*FROM(SELECT d.*FROM(SELECT c.*,@rownum :=@rownum +1AS rank
FROM buildbrain.`commits`AS c,(SELECT@rownum :=0)AS r,(SELECT@cnt :=(SELECT RAND()*(SELECT COUNT(*)FROM buildbrain.`commits`)))AS rnd
) d
WHERE rank >=@cnt LIMIT 10000) t ORDERBY RAND() LIMIT 10;
Salah satu cara yang saya temukan cukup baik jika ada id yang di-autogenerasi adalah dengan menggunakan operator modulo '%'. Misalnya, jika Anda memerlukan 10.000 catatan acak dari 70.000, Anda dapat menyederhanakan ini dengan mengatakan Anda perlu 1 dari setiap 7 baris. Ini dapat disederhanakan dalam kueri ini:
SELECT*FROMtableWHERE
id %
FLOOR((SELECT count(1)FROMtable)/10000)=0;
Jika hasil membagi baris target dengan total yang tersedia bukan bilangan bulat, Anda akan memiliki beberapa baris tambahan dari yang Anda minta, jadi Anda harus menambahkan klausa LIMIT untuk membantu Anda memotong set hasil seperti ini:
SELECT*FROMtableWHERE
id %
FLOOR((SELECT count(1)FROMtable)/10000)=0
LIMIT 10000;
Ini memang membutuhkan pemindaian penuh, tetapi lebih cepat dari ORDER BY RAND, dan menurut saya lebih mudah dimengerti daripada opsi lain yang disebutkan dalam utas ini. Juga jika sistem yang menulis ke DB membuat kumpulan baris dalam batch Anda mungkin tidak mendapatkan hasil acak seperti yang Anda harapkan.
Sekarang saya pikir begitu, jika Anda membutuhkan baris acak setiap kali Anda menyebutnya, ini tidak berguna. Saya hanya berpikir tentang perlunya mendapatkan baris acak dari set untuk melakukan penelitian. Saya masih berpikir modulo adalah hal yang baik untuk membantu dalam kasus lain. Anda dapat menggunakan modulo sebagai filter pass pertama untuk menurunkan biaya operasi ORDER BY RAND.
Nicolas Cohen
1
Jika Anda ingin satu catatan acak (tidak masalah jika ada kesenjangan antara id):
Saya telah memeriksa semua jawaban, dan saya tidak berpikir ada yang menyebutkan kemungkinan ini sama sekali, dan saya tidak yakin mengapa.
Jika Anda ingin kesederhanaan dan kecepatan maksimal, dengan biaya rendah, maka bagi saya tampaknya masuk akal untuk menyimpan angka acak terhadap setiap baris dalam DB. Cukup buat kolom tambahan random_number,, dan tetapkan default ke RAND(). Buat indeks pada kolom ini.
Kemudian ketika Anda ingin mengambil baris, buat angka acak dalam kode Anda (PHP, Perl, apa pun) dan bandingkan dengan kolom.
SELECT FROM tbl WHERE random_number >= :random LIMIT 1
Saya kira meskipun sangat rapi untuk satu baris, untuk sepuluh baris seperti OP meminta Anda harus menyebutnya sepuluh kali terpisah (atau muncul dengan tweak pintar yang segera lolos dari saya)
Ini sebenarnya pendekatan yang sangat bagus dan efisien. Satu-satunya kelemahan adalah fakta bahwa Anda menukar ruang untuk kecepatan, yang sepertinya merupakan kesepakatan yang adil menurut saya.
Tochukwu Nkemdilim
Terima kasih. Saya mempunyai skenario di mana tabel utama saya ingin baris acak dari memiliki 5 juta baris, dan cukup banyak yang bergabung, dan setelah mencoba sebagian besar pendekatan dalam pertanyaan ini ini adalah kludge yang saya menetap. Satu kolom tambahan adalah pertukaran yang sangat berharga bagi saya.
Codemonkey
0
Berikut ini harus cepat, tidak bias dan independen dari kolom id. Namun itu tidak menjamin bahwa jumlah baris yang dikembalikan akan cocok dengan jumlah baris yang diminta.
SELECT*FROM t
WHERE RAND()<(SELECT10/ COUNT(*)FROM t)
Penjelasan: dengan asumsi Anda ingin 10 baris dari 100 maka setiap baris memiliki 1/10 kemungkinan mendapatkan SELECT yang dapat dicapai oleh WHERE RAND() < 0.1. Pendekatan ini tidak menjamin 10 baris; tetapi jika kueri dijalankan cukup kali jumlah rata-rata baris per eksekusi akan sekitar 10 dan setiap baris dalam tabel akan dipilih secara merata.
Anda juga dapat menerapkan klausa tempat seperti itu
PREPARE stm from'select * from table where available=true limit 10 offset ?';SET@total =(select count(*)fromtablewhere available=true);SET@_offset = FLOOR(RAND()*@total);EXECUTE stm using@_offset;
Diuji pada 600.000 baris (700MB) tabel eksekusi query mengambil ~ 0.016sec HDD Drive
EDIT
offset mungkin mengambil nilai dekat dengan ujung meja, yang akan menghasilkan pernyataan pilih kembali kurang baris (atau mungkin hanya 1 baris), untuk menghindari ini kita dapat memeriksa offsetlagi setelah mendeklarasikannya, seperti itu
Sial, tidak, itu salah satu cara terburuk untuk mendapatkan baris acak dari tabel. Pemindaian tabel lengkap + filesort + tmp tabel = kinerja buruk.
matt
1
Selain kinerja, itu juga jauh dari acak; Anda memesan dengan produk dari id dan nomor acak, bukan hanya memesan dengan nomor acak, yang berarti bahwa baris dengan id lebih rendah akan cenderung muncul lebih awal di set hasil Anda.
Jawaban:
Pos besar yang menangani beberapa kasus, dari yang sederhana, hingga yang kosong, hingga yang tidak seragam dengan celah.
http://jan.kneschke.de/projects/mysql/order-by-rand/
Untuk sebagian besar kasus umum, inilah cara Anda melakukannya:
Ini mengandaikan bahwa distribusi id adalah sama, dan bahwa mungkin ada kesenjangan dalam daftar id. Lihat artikel untuk contoh lebih lanjut
sumber
mysqli_fetch_assoc($result)
? Atau apakah 10 hasil itu belum tentu dapat dibedakan?Bukan solusi yang efisien tetapi bekerja
sumber
ORDER BY RAND()
relatif lambatSELECT words, transcription, translation, sound FROM vocabulary WHERE menu_id=$menuId ORDER BY RAND() LIMIT 10
butuh 0,0010, tanpa LIMIT 10 butuh 0,0012 (dalam tabel itu 3500 kata).Permintaan sederhana yang memiliki kinerja luar biasa dan bekerja dengan kesenjangan :
Kueri ini pada tabel 200K memakan waktu 0,08s dan versi normal (PILIH * DARI ORDER tbl DENGAN RAND () LIMIT 10) membutuhkan 0,35s pada mesin saya.
Ini cepat karena fase pengurutan hanya menggunakan kolom ID yang diindeks. Anda dapat melihat perilaku ini di penjelasan:
PILIH * DARI Tbl ORDER OLEH RAND () LIMIT 10:
SELECT * DARI tbl AS t1 BERGABUNG (SELECT id FROM tbl ORDER BY RAND () LIMIT 10) sebagai t2 ON t1.id = t2.id
Versi Tertimbang : https://stackoverflow.com/a/41577458/893432
sumber
Saya mendapatkan pertanyaan cepat (sekitar 0,5 detik) dengan cpu lambat , memilih 10 baris acak dalam 400K register database MySQL ukuran 2Gb non-cache. Lihat di sini kode saya: Pilihan cepat baris acak di MySQL
sumber
ORDER BY RAND()
FLUSH STATUS; SELECT ...; SHOW SESSION STATUS LIKE 'Handler%';
untuk melihatnya.ORDER BY RAND()
adalah bahwa ia hanya mengurutkan id (bukan baris penuh), sehingga tabel temp lebih kecil, tetapi masih harus mengurutkan semuanya.Permintaan baris yang sangat sederhana dan tunggal.
sumber
order by rand()
sangat lambat jika meja besarDari buku:
Pilih Baris Acak Menggunakan Offset
Masih teknik lain yang menghindari masalah yang ditemukan dalam alternatif sebelumnya adalah menghitung baris dalam kumpulan data dan mengembalikan angka acak antara 0 dan hitungan. Kemudian gunakan nomor ini sebagai offset saat menanyakan kumpulan data
Gunakan solusi ini ketika Anda tidak dapat mengasumsikan nilai kunci yang berdekatan dan Anda perlu memastikan setiap baris memiliki peluang yang sama untuk dipilih.
sumber
SELECT count(*)
menjadi lambat.Cara memilih baris acak dari tabel:
Dari sini: Pilih baris acak di MySQL
Peningkatan cepat atas "pemindaian tabel" adalah dengan menggunakan indeks untuk mengambil id acak.
sumber
PRIMARY KEY
).Nah, jika Anda tidak memiliki celah pada kunci Anda dan semuanya berupa angka, Anda dapat menghitung angka acak dan memilih garis itu. tetapi ini mungkin tidak akan terjadi.
Jadi satu solusi adalah sebagai berikut:
yang pada dasarnya akan memastikan bahwa Anda mendapatkan nomor acak dalam kisaran kunci Anda dan kemudian Anda memilih yang terbaik berikutnya yang lebih besar. Anda harus melakukan ini 10 kali.
namun ini TIDAK benar-benar acak karena kunci Anda kemungkinan besar tidak akan didistribusikan secara merata.
Ini benar-benar masalah besar dan tidak mudah untuk menyelesaikan semua persyaratan, rand MySQL () adalah yang terbaik yang bisa Anda dapatkan jika Anda benar-benar menginginkan 10 baris acak.
Namun ada solusi lain yang cepat tetapi juga memiliki trade off ketika datang ke keacakan, tetapi mungkin lebih cocok untuk Anda. Baca tentang ini di sini: Bagaimana saya bisa mengoptimalkan fungsi ORDER BY RAND () MySQL?
Pertanyaannya adalah seberapa acak Anda membutuhkannya?
Bisakah Anda menjelaskan lebih banyak sehingga saya bisa memberikan solusi yang baik.
Sebagai contoh, sebuah perusahaan tempat saya bekerja memiliki solusi di mana mereka membutuhkan keacakan mutlak sangat cepat. Mereka berakhir dengan pra-mengisi database dengan nilai acak yang dipilih turun dan diatur ke nilai acak yang berbeda setelah itu lagi.
Jika Anda hampir tidak pernah memperbarui Anda juga bisa mengisi id tambahan sehingga Anda tidak memiliki celah dan hanya dapat menghitung kunci acak sebelum memilih ... Itu tergantung pada kasus penggunaan!
sumber
Id
dan semua kueri acak Anda akan mengembalikan yang ituId
.FLOOR(RAND()*MAX(id))
bias terhadap pengembalian id yang lebih besar.Saya membutuhkan kueri untuk mengembalikan sejumlah besar baris acak dari tabel yang agak besar. Inilah yang saya pikirkan. Pertama-tama dapatkan id rekaman maksimum:
Kemudian gantilah nilai itu menjadi:
Di mana max adalah id rekaman maksimum dalam tabel dan n adalah jumlah baris yang Anda inginkan di set hasil Anda. Asumsinya adalah bahwa tidak ada celah dalam id rekaman meskipun saya ragu itu akan mempengaruhi hasilnya jika ada (belum mencobanya). Saya juga membuat prosedur tersimpan ini menjadi lebih umum; masukkan nama tabel dan jumlah baris yang akan dikembalikan. Saya menjalankan MySQL 5.5.38 pada Windows 2008, 32GB, dual 3GHz E5450, dan di atas meja dengan 17.361.264 baris cukup konsisten pada ~ .03 detik / ~ 11 detik untuk mengembalikan 1.000.000 baris. (kali dari MySQL Workbench 6.1; Anda juga bisa menggunakan CEIL alih-alih LANTAI dalam pernyataan pilih kedua tergantung pada preferensi Anda)
kemudian
sumber
Semua jawaban terbaik telah diposting (terutama yang mereferensikan tautan http://jan.kneschke.de/projects/mysql/order-by-rand/ ).
Saya ingin menunjukkan kemungkinan percepatan lain - caching . Pikirkan mengapa Anda perlu mendapatkan baris acak. Mungkin Anda ingin menampilkan beberapa posting acak atau iklan acak di situs web. Jika Anda mendapatkan 100 req / s, apakah benar-benar diperlukan setiap pengunjung mendapatkan baris acak? Biasanya baik-baik saja untuk men-cache X ini baris acak selama 1 detik (atau bahkan 10 detik) Tidak masalah jika 100 pengunjung unik dalam 1 detik yang sama mendapatkan posting acak yang sama, karena detik berikutnya 100 pengunjung lainnya akan mendapatkan serangkaian posting yang berbeda.
Saat menggunakan caching ini, Anda juga dapat menggunakan beberapa solusi yang lebih lambat untuk mendapatkan data acak karena akan diambil dari MySQL hanya sekali per detik terlepas dari kebutuhan Anda.
sumber
Saya memperbaiki jawaban yang dimiliki @Riedsio. Ini adalah kueri paling efisien yang dapat saya temukan pada tabel besar, terdistribusi secara merata dengan celah (diuji untuk mendapatkan 1000 baris acak dari tabel yang memiliki> baris 2.6B).
Biarkan saya membongkar apa yang terjadi.
@max := (SELECT MAX(id) FROM table)
MAX(id)
setiap kali Anda membutuhkan satu barisSELECT FLOOR(rand() * @max) + 1 as rand)
SELECT id FROM table INNER JOIN (...) on id > rand LIMIT 1
Melakukan penyatuan membantu Anda memasukkan semuanya ke dalam 1 kueri sehingga Anda dapat menghindari melakukan beberapa kueri. Ini juga memungkinkan Anda menghemat biaya perhitungan
MAX(id)
. Tergantung pada aplikasi Anda, ini mungkin penting atau sangat sedikit.Perhatikan bahwa ini hanya mendapatkan id dan membuatnya secara acak. Jika Anda ingin melakukan sesuatu yang lebih maju, saya sarankan Anda melakukan ini:
sumber
LIMIT 1
keLIMIT 30
mana - mana dalam permintaanLIMIT 1
keLIMIT 30
akan membuat Anda 30 catatan berturut-turut dari titik acak di tabel. Anda seharusnya memiliki 30 salinan(SELECT id FROM ....
bagian di tengah.Riedsio
menjawab. Saya telah mencoba dengan 500 hit per halaman ke halaman menggunakan PHP 7.0.22 dan MariaDB pada centos 7, denganRiedsio
jawaban saya mendapat 500+ respon ekstra sukses maka jawaban Anda.Saya menggunakan http://jan.kneschke.de/projects/mysql/order-by-rand/ yang diposting oleh Riedsio (saya menggunakan kasus prosedur tersimpan yang mengembalikan satu atau lebih nilai acak):
Dalam artikel tersebut ia memecahkan masalah kesenjangan dalam id yang menyebabkan hasil tidak begitu acak dengan mempertahankan tabel (menggunakan pemicu, dll ... lihat artikel); Saya memecahkan masalah dengan menambahkan kolom lain ke tabel, diisi dengan angka yang berdekatan, mulai dari 1 ( edit: kolom ini ditambahkan ke tabel sementara yang dibuat oleh subquery saat runtime, tidak mempengaruhi tabel permanen Anda):
Dalam artikel itu saya bisa melihat dia berusaha keras untuk mengoptimalkan kode; saya tidak tahu jika / seberapa besar perubahan saya berdampak pada kinerja tetapi bekerja sangat baik untuk saya.
sumber
@no_gaps_id
indeks tidak dapat digunakan, jadi jika Anda melihatEXPLAIN
permintaan Anda, Anda memilikiUsing filesort
danUsing where
(tanpa indeks) untuk subqueries, berbeda dengan permintaan asli.Berikut adalah pengubah permainan yang mungkin bermanfaat bagi banyak orang;
Saya memiliki tabel dengan 200k baris, dengan id berurutan , saya harus memilih N baris acak, jadi saya memilih untuk menghasilkan nilai acak berdasarkan ID terbesar dalam tabel, saya membuat skrip ini untuk mencari tahu mana yang merupakan operasi tercepat:
Hasilnya adalah:
36.8418693542479
ms0.241041183472
ms0.216960906982
msBerdasarkan hasil ini, order desc adalah operasi tercepat untuk mendapatkan max id,
Ini jawaban saya untuk pertanyaan:
FYI: Untuk mendapatkan 10 baris acak dari tabel 200k, butuh 1,78 ms (termasuk semua operasi di sisi php)
sumber
LIMIT
sedikit - Anda bisa mendapatkan duplikat.Ini sangat cepat dan 100% acak bahkan jika Anda memiliki celah.
x
baris yang Anda milikiSELECT COUNT(*) as rows FROM TABLE
a_1,a_2,...,a_10
antara 0 danx
SELECT * FROM TABLE LIMIT 1 offset a_i
untuk i = 1, ..., 10Saya menemukan hack ini di buku SQL Antipatterns dari Bill Karwin .
sumber
SELECT column FROM table ORDER BY RAND() LIMIT 10
ada di O (nlog (n)). Jadi ya, ini adalah solusi puasa dan berfungsi untuk setiap distribusi id.x
. Saya berpendapat bahwa ini bukan generasi acak 10 baris. Dalam jawaban saya, Anda harus menjalankan kueri di langkah tiga 10 kali, yaitu satu hanya mendapat satu baris per eksekusi dan tidak perlu khawatir jika offset ada di akhir tabel.Jika Anda hanya memiliki satu Permintaan Baca
Gabungkan jawaban @redsio dengan temp-table (600K tidak terlalu banyak):
Dan kemudian ambil versi @redsios Jawaban:
Jika meja besar, Anda dapat mengayak pada bagian pertama:
Jika Anda memiliki banyak permintaan baca
Versi: Anda bisa menyimpan tabel
tmp_randorder
tetap ada, sebut saja datatable_idlist. Buat ulang tabel itu dalam interval tertentu (hari, jam), karena meja juga akan berlubang. Jika meja Anda menjadi sangat besar, Anda juga bisa mengisi ulang lubangpilih l.data_id secara keseluruhan dari datatable_idlist l kiri gabung datatable dt di dt.id = l.data_id di mana dt.id bernilai null;
Versi: Berikan Dataset Anda sebuah kolom random_sortorder baik secara langsung di datatable atau dalam tabel ekstra persisten
datatable_sortorder
. Buat indeks kolom itu. Hasilkan Nilai Acak di Aplikasi Anda (saya akan menyebutnya$rand
).Solusi ini membedakan 'baris tepi' dengan urutan random_sort tertinggi dan terendah, jadi atur ulangnya dalam interval (sekali sehari).
sumber
Solusi sederhana lain adalah memberi peringkat pada baris dan mengambil salah satunya secara acak dan dengan solusi ini Anda tidak perlu memiliki kolom berdasarkan 'Id' di tabel.
Anda dapat mengubah nilai batas sesuai kebutuhan Anda untuk mengakses baris sebanyak yang Anda inginkan tetapi itu sebagian besar akan menjadi nilai berturut-turut.
Namun, jika Anda tidak ingin nilai acak berturut-turut maka Anda dapat mengambil sampel yang lebih besar dan memilih secara acak dari itu. sesuatu seperti ...
sumber
Salah satu cara yang saya temukan cukup baik jika ada id yang di-autogenerasi adalah dengan menggunakan operator modulo '%'. Misalnya, jika Anda memerlukan 10.000 catatan acak dari 70.000, Anda dapat menyederhanakan ini dengan mengatakan Anda perlu 1 dari setiap 7 baris. Ini dapat disederhanakan dalam kueri ini:
Jika hasil membagi baris target dengan total yang tersedia bukan bilangan bulat, Anda akan memiliki beberapa baris tambahan dari yang Anda minta, jadi Anda harus menambahkan klausa LIMIT untuk membantu Anda memotong set hasil seperti ini:
Ini memang membutuhkan pemindaian penuh, tetapi lebih cepat dari ORDER BY RAND, dan menurut saya lebih mudah dimengerti daripada opsi lain yang disebutkan dalam utas ini. Juga jika sistem yang menulis ke DB membuat kumpulan baris dalam batch Anda mungkin tidak mendapatkan hasil acak seperti yang Anda harapkan.
sumber
Jika Anda ingin satu catatan acak (tidak masalah jika ada kesenjangan antara id):
Sumber: https://www.warpconduit.net/2011/03/23/selected-a-random-record-using-mysql-benchmark-results/#comment-1266
sumber
Saya telah memeriksa semua jawaban, dan saya tidak berpikir ada yang menyebutkan kemungkinan ini sama sekali, dan saya tidak yakin mengapa.
Jika Anda ingin kesederhanaan dan kecepatan maksimal, dengan biaya rendah, maka bagi saya tampaknya masuk akal untuk menyimpan angka acak terhadap setiap baris dalam DB. Cukup buat kolom tambahan
random_number
,, dan tetapkan default keRAND()
. Buat indeks pada kolom ini.Kemudian ketika Anda ingin mengambil baris, buat angka acak dalam kode Anda (PHP, Perl, apa pun) dan bandingkan dengan kolom.
SELECT FROM tbl WHERE random_number >= :random LIMIT 1
Saya kira meskipun sangat rapi untuk satu baris, untuk sepuluh baris seperti OP meminta Anda harus menyebutnya sepuluh kali terpisah (atau muncul dengan tweak pintar yang segera lolos dari saya)
sumber
Berikut ini harus cepat, tidak bias dan independen dari kolom id. Namun itu tidak menjamin bahwa jumlah baris yang dikembalikan akan cocok dengan jumlah baris yang diminta.
Penjelasan: dengan asumsi Anda ingin 10 baris dari 100 maka setiap baris memiliki 1/10 kemungkinan mendapatkan SELECT yang dapat dicapai oleh
WHERE RAND() < 0.1
. Pendekatan ini tidak menjamin 10 baris; tetapi jika kueri dijalankan cukup kali jumlah rata-rata baris per eksekusi akan sekitar 10 dan setiap baris dalam tabel akan dipilih secara merata.sumber
Anda dapat dengan mudah menggunakan offset acak dengan batas
Anda juga dapat menerapkan klausa tempat seperti itu
Diuji pada 600.000 baris (700MB) tabel eksekusi query mengambil ~ 0.016sec HDD Drive
EDIT
offset mungkin mengambil nilai dekat dengan ujung meja, yang akan menghasilkan pernyataan pilih kembali kurang baris (atau mungkin hanya 1 baris), untuk menghindari ini kita dapat memeriksa
offset
lagi setelah mendeklarasikannya, seperti itusumber
Saya Menggunakan kueri ini:
waktu permintaan: 0,016s
sumber
Beginilah cara saya melakukannya:
Saya suka karena tidak memerlukan tabel lain, mudah untuk menulis, dan sangat cepat untuk dieksekusi.
sumber
Gunakan kueri sederhana di bawah ini untuk mendapatkan data acak dari tabel.
sumber
Saya kira ini adalah cara terbaik yang mungkin ..
sumber