Yanıtlar:
Basit, boşluklara, boşluklarla üniform olmayanlara kadar çeşitli durumları ele alan harika bir yazı.
http://jan.kneschke.de/projects/mysql/order-by-rand/
Çoğu genel durum için, bunu nasıl yapacağınız aşağıda açıklanmıştır:
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
Bu, id'lerin dağılımının eşit olduğunu ve id listesinde boşluklar olabileceğini varsayar. Daha gelişmiş örnekler için makaleye bakın
mysqli_fetch_assoc($result)
mi? Yoksa bu 10 sonuç mutlaka ayırt edilemez mi?
SELECT column FROM table
ORDER BY RAND()
LIMIT 10
Etkin çözüm değil, işe yarıyor
ORDER BY RAND()
nispeten yavaş
SELECT words, transcription, translation, sound FROM vocabulary WHERE menu_id=$menuId ORDER BY RAND() LIMIT 10
0.0010 alır, LIMIT 10 olmadan 0.0012 aldı (bu tabloda 3500 kelime).
Mükemmel performansa sahip ve boşluklarla çalışan basit sorgu :
SELECT * FROM tbl AS t1 JOIN (SELECT id FROM tbl ORDER BY RAND() LIMIT 10) as t2 ON t1.id=t2.id
Bir 200K masaya Bu sorgu alır 0.08s ve (RAND () LIMIT 10 BY tbl SİPARİŞ SEÇ *) Normal versiyonunu alır 0.35s benim makinede.
Sıralama aşaması yalnızca dizinlenmiş kimlik sütununu kullandığından bu hızlıdır. Bu davranışı aşağıdaki açıklamada görebilirsiniz:
Rbl TARAFINDAN SİPARİŞ SEÇİN () LİMİT 10:
SEÇİM * t1'DEN ASLA tbl BİRLEŞİM (tbl ORDER BY RAND () LIMIT 10'dan KİMLİĞİ seç) t2 AÇIK t1.id = t2.id olarak
Ağırlıklı Sürüm : https://stackoverflow.com/a/41577458/893432
400 K kaydeder MySQL veritabanı önbelleğe alınmamış 2 Gb boyutunda 10 rastgele satır seçerek, yavaş bir cpu ile hızlı sorgular (yaklaşık 0.5 saniye) alıyorum . Kodum: MySQL'de rasgele satırların hızlı seçimi
<?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%';
Görmek için kullanın .
ORDER BY RAND()
sadece kimlikleri (tam satırları değil) sıralamasıdır, bu nedenle geçici tablo daha küçüktür, ancak yine de hepsini sıralamak zorundadır.
Çok basit ve tek satırlı sorgu.
SELECT * FROM Table_Name ORDER BY RAND() LIMIT 0,10;
order by rand()
masa büyükse çok yavaş
Kitaptan:
Bir Ofset Kullanarak Rastgele Bir Satır Seçin
Önceki alternatiflerde bulunan problemlerden kaçınan başka bir teknik, veri kümesindeki satırları saymak ve 0 ile sayı arasında rastgele bir sayı döndürmektir. Ardından, veri kümesini sorgularken bu sayıyı ofset olarak kullanın
<?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();
Bitişik anahtar değerleri üstlenemediğinizde ve her satırın seçilme şansının eşit olduğundan emin olmanız gerektiğinde bu çözümü kullanın.
SELECT count(*)
yavaşlar.
Bir tablodan rastgele satırlar nasıl seçilir:
Buradan: MySQL'de rastgele satırlar seçin
"Tablo taraması" üzerinde hızlı bir gelişme, rastgele kimlikleri almak için dizini kullanmaktır.
SELECT *
FROM random, (
SELECT id AS sid
FROM random
ORDER BY RAND( )
LIMIT 10
) tmp
WHERE random.id = tmp.sid;
PRIMARY KEY
).
Anahtarlarınızda boşluk yoksa ve hepsi sayısalsa, rastgele sayıları hesaplayabilir ve bu satırları seçebilirsiniz. ama muhtemelen böyle olmayacak.
Yani bir çözüm şudur:
SELECT * FROM table WHERE key >= FLOOR(RAND()*MAX(id)) LIMIT 1
Bu, temel olarak anahtarlarınızın aralığında rastgele bir sayı almanızı ve daha sonra bir sonraki en iyi olanı seçmenizi sağlar. bunu 10 kez yapmak zorundasınız.
Ancak bu gerçekten rastgele DEĞİLDİR çünkü anahtarlarınız büyük olasılıkla eşit olarak dağıtılmayacaktır.
Gerçekten büyük bir sorun ve tüm gereksinimleri yerine getirmek kolay değil, MySQL'in rand () gerçekten 10 rastgele satır istiyorsanız elde edebileceğiniz en iyisidir.
Bununla birlikte, hızlı ancak rastgele olma konusunda bir ticarete sahip olan, ancak size daha uygun olabilecek başka bir çözüm var. Burada okuyun: MySQL'in ORDER BY RAND () fonksiyonunu nasıl optimize edebilirim?
Soru, ne kadar rastgele olmanız gerektiğidir.
Size daha iyi bir çözüm sunabilmek için biraz daha açıklayabilir misiniz?
Mesela birlikte çalıştığım bir şirketin mutlak rasgeleliğe son derece hızlı ihtiyaç duyduğu bir çözümü vardı. Veritabanını, azalan ve daha sonra tekrar farklı rastgele değerlere ayarlanmış rastgele değerlerle önceden doldurmakla sonuçlandılar.
Eğer neredeyse hiç güncelleme yapmazsanız, artan bir kimliği doldurabilirsiniz, böylece boşluk kalmazsınız ve seçmeden önce rastgele tuşları hesaplayabilirsiniz ... Kullanım durumuna bağlıdır!
Id
ve tüm rastgele sorgularınız size bunu döndürecektir Id
.
FLOOR(RAND()*MAX(id))
, daha büyük kimlikleri döndürmeye eğilimlidir.
Oldukça büyük bir tablodan çok sayıda rastgele satır döndürmek için bir sorgu gerekli. Ben de bunu buldum. İlk önce maksimum kayıt kimliğini alın:
SELECT MAX(id) FROM table_name;
Ardından bu değeri aşağıdaki gibi değiştirin:
SELECT * FROM table_name WHERE id > FLOOR(RAND() * max) LIMIT n;
Max ifadesinin tablodaki maksimum kayıt kimliği ve n sonuç kümenizde olmasını istediğiniz satır sayısıdır. Varsayım, kayıt kimliğinde hiçbir boşluk bulunmadığı halde (eğer denemediysem) sonucu etkileyeceğinden şüphe duyuyorum. Ayrıca bu saklı yordamı daha genel olması için oluşturdu; Tablo adını ve döndürülecek satır sayısını girin. MySQL 5.5.38'i Windows 2008, 32GB, çift 3GHz E5450'de çalıştırıyorum ve 17.361.264 satır içeren bir tabloda 1.000.000 satır döndürmek için ~ .03 sn / ~ 11 saniyede oldukça tutarlı. (süreler MySQL Workbench 6.1'den alınmıştır; tercihinize bağlı olarak 2. seçim deyiminde FLOOR yerine CEIL kullanabilirsiniz)
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
$$
sonra
CALL [schema name].random_rows([table name], n);
Tüm en iyi cevaplar zaten yayınlanmıştır (esas olarak http://jan.kneschke.de/projects/mysql/order-by-rand/ bağlantısına atıfta bulunanlar ).
Başka bir hızlanma olasılığını belirlemek istiyorum - önbellekleme . Neden rastgele satırlar almanız gerektiğini düşünün. Muhtemelen bir web sitesinde rastgele yazı veya rastgele reklam görüntülemek istiyorsunuz. 100 req / s alıyorsanız, her ziyaretçinin rastgele satır alması gerçekten gerekli mi? Genellikle bu X rastgele satırlarını 1 saniye (hatta 10 saniye) önbelleğe almak tamamen iyidir. Aynı 1 saniyedeki 100 benzersiz ziyaretçinin aynı rastgele gönderileri alması önemli değildir, çünkü bir sonraki ikinci 100 ziyaretçinin farklı gönderiler alması gerekir.
Bu önbelleği kullanırken, rasgele verileri almak için daha yavaş çözümlerden bazılarını kullanabilirsiniz, çünkü bu gereksinimleriniz ne olursa olsun MySQL'den saniyede sadece bir kez alınacaktır.
@Riedsio'nun cevabını geliştirdim. Bu, boşlukları olan büyük, tekdüze bir şekilde dağıtılmış bir tabloda bulabildiğim en etkili sorgudur (> 2.6B satırları olan bir tablodan 1000 rasgele satır elde edilmesinde test edilmiştir).
(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)
Neler olup bittiğini açayım.
@max := (SELECT MAX(id) FROM table)
MAX(id)
bir satıra ihtiyacınız olduğunda hesaplamak için hafif bir ek yük vardırSELECT FLOOR(rand() * @max) + 1 as rand)
SELECT id FROM table INNER JOIN (...) on id > rand LIMIT 1
Birliğin yapılması, her şeyi 1 sorguya sığdırmanıza yardımcı olur, böylece birden çok sorgu yapmaktan kaçınabilirsiniz. Ayrıca hesaplama yükünü de kaydetmenizi sağlar MAX(id)
. Uygulamanıza bağlı olarak, bu çok veya çok az önemli olabilir.
Bunun yalnızca kimlikleri ve rastgele sırada aldığını unutmayın. Daha gelişmiş bir şey yapmak istiyorsanız, bunu yapmanızı tavsiye ederim:
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
için LIMIT 30
sorguda her yerde
LIMIT 1
için LIMIT 30
tablodaki rastgele bir noktadan size arka arkaya 30 kayıtları tanınacak. Bunun yerine (SELECT id FROM ....
ortasındaki parçanın 30 kopyasına sahip olmalısınız .
Riedsio
cevap daha verimli görünmüyor . Ben centos 7 PHP 7.0.22 ve MariaDB kullanarak sayfaya saniyede 500 isabet denedim, Riedsio
cevap ile cevap sonra 500+ ekstra başarılı yanıt var.
Riedsio tarafından gönderilen bu http://jan.kneschke.de/projects/mysql/order-by-rand/ kullandım (bir veya daha fazla rastgele değer döndüren saklı yordam durumunda kullandım):
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;
Makalede de çözer boşlukların sorunu neden kimlikleri de o kadar rasgele sonuçlar (... makaleye bakın, vb tetikleyiciler kullanılarak) bir tablo koruyarak; 1'den başlayarak bitişik sayılarla doldurulmuş tabloya başka bir sütun ekleyerek sorunu çözüyorum ( düzenle: bu sütun, çalışma zamanında alt sorgu tarafından oluşturulan geçici tabloya eklenir, kalıcı tablonuzu etkilemez):
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;
Makalede kodu optimize etmek için büyük uzunluklara gittiğini görebiliyorum; Değişikliklerimin performansı etkilediği, benim için çok iyi çalıştığı durumlarda hiçbir ideeam yok.
@no_gaps_id
hiçbir indeksi kullanılabilir sen bakarsanız, bu yüzden EXPLAIN
sizin sorgusu için, sahip Using filesort
ve Using where
özgün sorgusuna aksine, alt sorgular için (index olmadan).
İşte birçoğu için yararlı olabilecek bir oyun değiştirici;
Ben sıralı id ile 200k satır içeren bir tablo var , ben N rastgele satırları seçmek gerekiyordu , bu yüzden tablodaki en büyük kimliğe dayalı rastgele değerler oluşturmak için tercih, en hızlı işlem hangisi bulmak için bu komut dosyası oluşturdu:
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();
Sonuçlar:
36.8418693542479
ms0.241041183472
ms0.216960906982
msBu sonuçlara dayanarak, order desc maksimum kimliği almak için en hızlı işlemdir,
İşte soruya cevabım:
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);
Bilginize: 200k'luk bir tablodan 10 rastgele satır almak için, beni 1.78 ms aldı (php tarafındaki tüm işlemler dahil)
LIMIT
Hafifçe artırmanızı önerin - kopyaları alabilirsiniz.
Bu süper hızlı ve boşluklarınız olsa bile% 100 rastgele.
x
Kullanabileceğiniz satır sayısını sayınSELECT COUNT(*) as rows FROM TABLE
a_1,a_2,...,a_10
0 ile 0 arasında 10 farklı rastgele sayı seçinx
SELECT * FROM TABLE LIMIT 1 offset a_i
for i = 1, ..., 10Kitapta bu kesmek bulundu SQL Antipatterns gelen Bill Karwin .
SELECT column FROM table ORDER BY RAND() LIMIT 10
O (nlog (n))' dir. Yani evet, bu oruçlu çözümdür ve herhangi bir kimlik dağıtımı için çalışır.
x
. Bunun 10 sıradan oluşan rastgele bir nesil olmadığını iddia ediyorum. Cevabımda, üçüncü adımda 10 kez sorguyu yürütmek zorundasınız, yani bir yürütme başına yalnızca bir satır alır ve ofset tablonun sonundaysa endişelenmenize gerek yoktur.
@Redsio'nun cevabını bir geçici tabloyla birleştirin (600K o kadar değil):
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;
Sonra da @redsios Cevap sürümünü edinin:
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;
Tablo büyükse, ilk kısımda elek yapabilirsiniz:
INSERT INTO tmp_randorder (data_id) select id from datatable where rand() < 0.01;
Sürüm: Tabloyu tmp_randorder
kalıcı tutabilir, datatable_idlist olarak adlandırabilirsiniz. Bu tabloyu belirli aralıklarla (gün, saat) yeniden oluşturun, çünkü delikler de alır. Masanız gerçekten büyürse, delikleri de doldurabilirsiniz.
datatable_idlist'ten bütün olarak l.data_id öğesini seçin l dt.id üzerinde datatable dt'yi bir arada bırak = l.data_id; burada dt.id boştur;
Sürüm: Veri Kümenize, doğrudan veri tablosunda veya kalıcı bir ekstra tabloda rasgele_sortorder sütunu verin datatable_sortorder
. Bu sütunu dizine ekleyin. Başvurunuzda Rastgele Değer Üretin (onu arayacağım $rand
).
select l.*
from datatable l
order by abs(random_sortorder - $rand) desc
limit 1;
Bu çözüm, en yüksek ve en düşük random_sortorder ile 'kenar satırlarını' ayırt eder, bu nedenle bunları aralıklarla (günde bir kez) yeniden düzenleyin.
Başka bir basit çözüm, sıraları sıralamak ve bunlardan birini rastgele almaktır ve bu çözümle tabloda herhangi bir 'Id' tabanlı sütuna ihtiyacınız yoktur.
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;
Sınır değerini istediğiniz sayıda satıra erişme gereksiniminize göre değiştirebilirsiniz, ancak bu çoğunlukla ardışık değerler olur.
Ancak, ardışık rasgele değerler istemiyorsanız, daha büyük bir örnek alıp rastgele seçebilirsiniz. gibi bir şey ...
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;
Otomatik olarak oluşturulan bir kimlik varsa oldukça iyi bulmanın bir yolu modulo operatörü '%' kullanmaktır. Örneğin, 70.000 üzerinden 10.000 rasgele kayda ihtiyacınız varsa, her 7 satırdan 1'ine ihtiyacınız olduğunu söyleyerek bunu basitleştirebilirsiniz. Bu, bu sorguda basitleştirilebilir:
SELECT * FROM
table
WHERE
id %
FLOOR(
(SELECT count(1) FROM table)
/ 10000
) = 0;
Hedef satırları kullanılabilir toplamlara bölmenin sonucu bir tamsayı değilse, istediğinden daha fazla satırınız olacaktır, bu nedenle sonuç kümesini şu şekilde kırpmanıza yardımcı olacak bir LIMIT yan tümcesi eklemelisiniz:
SELECT * FROM
table
WHERE
id %
FLOOR(
(SELECT count(1) FROM table)
/ 10000
) = 0
LIMIT 10000;
Bu tam bir tarama gerektirir, ancak ORDER BY RAND'den daha hızlıdır ve bence bu konuda belirtilen diğer seçeneklerden daha basittir. Ayrıca, DB'ye yazan sistem, gruplar halinde satır kümeleri oluşturuyorsa, beklediğiniz gibi rastgele bir sonuç alamayabilirsiniz.
Bir rastgele kayıt istiyorsanız (id'ler arasında boşluklar olursa olsun):
PREPARE stmt FROM 'SELECT * FROM `table_name` LIMIT 1 OFFSET ?';
SET @count = (SELECT
FLOOR(RAND() * COUNT(*))
FROM `table_name`);
EXECUTE stmt USING @count;
Tüm cevapları inceledim ve kimsenin bu olasılıktan hiç bahsetmediğini sanmıyorum ve neden olduğundan emin değilim.
En üst düzeyde basitlik ve hız istiyorsanız, küçük bir maliyetle, o zaman bana göre DB'deki her satıra rastgele bir sayı depolamak mantıklı görünüyor. Fazladan bir sütun oluşturmanız random_number
ve varsayılan olarak ayarlamanız yeterlidir RAND()
. Bu sütunda bir dizin oluşturun.
Daha sonra bir satır almak istediğinizde kodunuzda rastgele bir sayı oluşturun (PHP, Perl, ne olursa olsun) ve bunu sütunla karşılaştırın.
SELECT FROM tbl WHERE random_number >= :random LIMIT 1
Tek bir satır için çok temiz olmasına rağmen, OP gibi on satır için on ayrı kez çağırmanız gerektiğini sordu (veya hemen beni kaçan akıllı bir tweak ile gel)
Aşağıdakiler hızlı, tarafsız ve kimlik sütunundan bağımsız olmalıdır. Ancak, döndürülen satır sayısının istenen satır sayısıyla eşleşeceğini garanti etmez.
SELECT *
FROM t
WHERE RAND() < (SELECT 10 / COUNT(*) FROM t)
Açıklama: 100'den 10 satır istediğinizi varsayarsak, her satırın 1/10 SELECTed alma olasılığı vardır ve bu da elde edilebilir WHERE RAND() < 0.1
. Bu yaklaşım 10 satırı garanti etmez; ancak sorgu yeterli sayıda çalıştırılırsa, yürütme başına ortalama satır sayısı yaklaşık 10 olur ve tablodaki her satır eşit olarak seçilir.
PREPARE stm from 'select * from table limit 10 offset ?';
SET @total = (select count(*) from table);
SET @_offset = FLOOR(RAND() * @total);
EXECUTE stm using @_offset;
Bunun gibi bir where cümlesi de uygulayabilirsiniz
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;
600.000 satır üzerinde test (700MB) tablo sorgu yürütme ~ 0.016sec HDD sürücü aldı
--EDIT--
belki bir değer daha az satır dönen select deyimi ile sonuçlanacaktır tablonun sonuna yakın, (veya tek 1 sürebilir ofset satır), bunu önlemek offset
için, beyan ettikten sonra tekrar kontrol edebiliriz ,
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;
Bu sorguyu kullanın:
select floor(RAND() * (SELECT MAX(key) FROM table)) from table limit 10
sorgu süresi: 0.016s
Bunu nasıl yaparım:
select *
from table_with_600k_rows
where rand() < 10/600000
limit 10
Diğer tabloları gerektirmediği için seviyorum, yazmak basit ve yürütmek çok hızlı.
Bir tablodan rastgele veri almak için aşağıdaki basit sorguyu kullanın.
SELECT user_firstname ,
COUNT(DISTINCT usr_fk_id) cnt
FROM userdetails
GROUP BY usr_fk_id
ORDER BY cnt ASC
LIMIT 10
Sanırım bu mümkün olan en iyi yol ...
SELECT id, id * RAND( ) AS random_no, first_name, last_name
FROM user
ORDER BY random_no