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:
SELECT name
FROM random AS r1 JOIN
(SELECT CEIL(RAND() *
(SELECT MAX(id)
FROM random)) AS id)
AS r2
WHERE r1.id >= r2.id
ORDER BY r1.id ASC
LIMIT 1
Ini mengandaikan bahwa distribusi id adalah sama, dan bahwa mungkin ada kesenjangan dalam daftar id. Lihat artikel untuk contoh lebih lanjut
mysqli_fetch_assoc($result)
? Atau apakah 10 hasil itu belum tentu dapat dibedakan?
SELECT column FROM table
ORDER BY RAND()
LIMIT 10
Bukan solusi yang efisien tetapi bekerja
ORDER BY RAND()
relatif lambat
SELECT 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 :
SELECT * FROM tbl AS t1 JOIN (SELECT id FROM tbl ORDER BY 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
Versi Tertimbang : https://stackoverflow.com/a/41577458/893432
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);
?>
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.
SELECT * FROM Table_Name ORDER BY RAND() LIMIT 0,10;
order by rand()
sangat lambat jika meja besar
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
<?php
$rand = "SELECT ROUND(RAND() * (SELECT COUNT(*) FROM Bugs))";
$offset = $pdo->query($rand)->fetch(PDO::FETCH_ASSOC);
$sql = "SELECT * FROM Bugs LIMIT 1 OFFSET :offset";
$stmt = $pdo->prepare($sql);
$stmt->execute( $offset );
$rand_bug = $stmt->fetch();
Gunakan solusi ini ketika Anda tidak dapat mengasumsikan nilai kunci yang berdekatan dan Anda perlu memastikan setiap baris memiliki peluang yang sama untuk dipilih.
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.
SELECT *
FROM random, (
SELECT id AS sid
FROM random
ORDER BY RAND( )
LIMIT 10
) tmp
WHERE random.id = tmp.sid;
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:
SELECT * FROM table WHERE key >= FLOOR(RAND()*MAX(id)) LIMIT 1
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!
Id
dan semua kueri acak Anda akan mengembalikan yang itu Id
.
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:
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)
DELIMITER $$
USE [schema name] $$
DROP PROCEDURE IF EXISTS `random_rows` $$
CREATE PROCEDURE `random_rows`(IN tab_name VARCHAR(64), IN num_rows INT)
BEGIN
SET @t = CONCAT('SET @max=(SELECT MAX(id) FROM ',tab_name,')');
PREPARE stmt FROM @t;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @t = CONCAT(
'SELECT * FROM ',
tab_name,
' WHERE id>FLOOR(RAND()*@max) LIMIT ',
num_rows);
PREPARE stmt FROM @t;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
$$
kemudian
CALL [schema name].random_rows([table name], n);
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.
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 FROM table INNER JOIN (SELECT FLOOR(RAND() * @max := (SELECT MAX(id) FROM table)) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1)
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:
SELECT t.id, t.name -- etc, etc
FROM table t
INNER JOIN (
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max := (SELECT MAX(id) FROM table)) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1)
) x ON x.id = t.id
ORDER BY t.id
LIMIT 1
ke LIMIT 30
mana - mana dalam permintaan
LIMIT 1
ke LIMIT 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, dengan Riedsio
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):
DROP TEMPORARY TABLE IF EXISTS rands;
CREATE TEMPORARY TABLE rands ( rand_id INT );
loop_me: LOOP
IF cnt < 1 THEN
LEAVE loop_me;
END IF;
INSERT INTO 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
ORDER BY 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 TABLE IF EXISTS rands;
CREATE TEMPORARY TABLE rands ( rand_id INT );
loop_me: LOOP
IF cnt < 1 THEN
LEAVE loop_me;
END IF;
SET @no_gaps_id := 0;
INSERT INTO rands
SELECT r1.id
FROM (SELECT id, @no_gaps_id := @no_gaps_id + 1 AS 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
ORDER BY 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.
@no_gaps_id
indeks tidak dapat digunakan, jadi jika Anda melihat EXPLAIN
permintaan Anda, Anda memiliki Using filesort
dan Using 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:
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:
36.8418693542479
ms0.241041183472
ms0.216960906982
msBerdasarkan 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 ORDER BY 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)
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 .
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.
Gabungkan jawaban @redsio dengan temp-table (600K tidak terlalu banyak):
DROP TEMPORARY TABLE IF EXISTS tmp_randorder;
CREATE TABLE tmp_randorder (id int(11) not null auto_increment primary key, data_id int(11));
INSERT INTO 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
INNER JOIN tmp_randorder rndo on rndo.id between rnd.id - 10 and rnd.id + 10
INNER JOIN datatable AS dt on dt.id = rndo.data_id
ORDER BY abs(rndo.id - rnd.id)
LIMIT 1;
Jika meja besar, Anda dapat mengayak pada bagian pertama:
INSERT INTO tmp_randorder (data_id) select id from datatable where rand() < 0.01;
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
order by 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 + 1 AS 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 + 1 AS 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 ORDER BY 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 * FROM
table
WHERE
id %
FLOOR(
(SELECT count(1) FROM table)
/ 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 * FROM
table
WHERE
id %
FLOOR(
(SELECT count(1) FROM table)
/ 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.
Jika Anda ingin satu catatan acak (tidak masalah jika ada kesenjangan antara id):
PREPARE stmt FROM 'SELECT * FROM `table_name` LIMIT 1 OFFSET ?';
SET @count = (SELECT
FLOOR(RAND() * COUNT(*))
FROM `table_name`);
EXECUTE stmt USING @count;
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)
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() < (SELECT 10 / 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.
PREPARE stm from 'select * from table limit 10 offset ?';
SET @total = (select count(*) from table);
SET @_offset = FLOOR(RAND() * @total);
EXECUTE stm using @_offset;
Anda juga dapat menerapkan klausa tempat seperti itu
PREPARE stm from 'select * from table where available=true limit 10 offset ?';
SET @total = (select count(*) from table where 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 offset
lagi setelah mendeklarasikannya, seperti itu
SET @rows_count = 10;
PREPARE stm from "select * from table where available=true limit ? offset ?";
SET @total = (select count(*) from table where available=true);
SET @_offset = FLOOR(RAND() * @total);
SET @_offset = (SELECT IF(@total-@_offset<@rows_count,@_offset-@rows_count,@_offset));
SET @_offset = (SELECT IF(@_offset<0,0,@_offset));
EXECUTE stm using @rows_count,@_offset;
Saya Menggunakan kueri ini:
select floor(RAND() * (SELECT MAX(key) FROM table)) from table limit 10
waktu permintaan: 0,016s
Beginilah cara saya melakukannya:
select *
from table_with_600k_rows
where rand() < 10/600000
limit 10
Saya suka karena tidak memerlukan tabel lain, mudah untuk menulis, dan sangat cepat untuk dieksekusi.
Gunakan kueri sederhana di bawah ini untuk mendapatkan data acak dari tabel.
SELECT user_firstname ,
COUNT(DISTINCT usr_fk_id) cnt
FROM userdetails
GROUP BY usr_fk_id
ORDER BY cnt ASC
LIMIT 10
Saya kira ini adalah cara terbaik yang mungkin ..
SELECT id, id * RAND( ) AS random_no, first_name, last_name
FROM user
ORDER BY random_no