जवाबों:
साधारण से लेकर अंतराल तक, गैप के साथ गैर-समान रूप से कई मामलों को संभालने वाला एक शानदार पोस्ट।
http://jan.kneschke.de/projects/mysql/order-by-rand/
अधिकांश सामान्य मामलों के लिए, यहां बताया गया है कि आप इसे कैसे करते हैं:
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
यह मानता है कि आईडी का वितरण बराबर है, और आईडी सूची में अंतराल हो सकता है। अधिक उन्नत उदाहरणों के लिए लेख देखें
mysqli_fetch_assoc($result)
? या क्या वे 10 परिणाम आवश्यक नहीं हैं?
SELECT column FROM table
ORDER BY RAND()
LIMIT 10
कुशल समाधान नहीं है लेकिन काम करता है
ORDER BY RAND()
अपेक्षाकृत धीमी गति से है
SELECT words, transcription, translation, sound FROM vocabulary WHERE menu_id=$menuId ORDER BY RAND() LIMIT 10
0.0010 लेता है, बिना LIMIT 10 यह 0.0012 लिया (उस तालिका में 3500 शब्द)।
सरल क्वेरी जिसमें उत्कृष्ट प्रदर्शन होता है और अंतराल के साथ काम करता है :
SELECT * FROM tbl AS t1 JOIN (SELECT id FROM tbl ORDER BY RAND() LIMIT 10) as t2 ON t1.id=t2.id
200K टेबल पर यह क्वेरी 0.08s और सामान्य वर्जन (SELECT * FROM tbl ORDER by RAND) (LIMIT 10) लेता है मेरी मशीन पर 0.35s ।
यह तेज़ है क्योंकि सॉर्ट चरण केवल अनुक्रमित आईडी कॉलम का उपयोग करता है। आप इस व्यवहार को व्याख्या में देख सकते हैं:
रैंड द्वारा * टीबीआर ऑर्डर से चयन करें () 10:
सेलेक्ट * FROM tbl AS t1 JOIN (SELECT id FROM tbl ORDER by RAND () LIMIT 10) as t2 ON t1.id = t2.id
भारित संस्करण : https://stackoverflow.com/a/41577458/893432
मैं धीमी सीपीयू के साथ तेजी से क्वेरी (लगभग 0.5 सेकंड) कर रहा हूं , 400K रजिस्टर में 10 यादृच्छिक पंक्तियों का चयन करके MySQL डेटाबेस नॉन-कैश्ड 2Gb आकार। यहां देखें मेरा कोड: 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%';
इसे देखने के लिए उपयोग करें ।
ORDER BY RAND()
से अधिक इस @ रमनप्लोडिनोव का लाभ यह है कि यह केवल आईडी (पूर्ण पंक्तियों नहीं) को छांटता है , इसलिए अस्थायी तालिका छोटी होती है, लेकिन फिर भी उन सभी को क्रमबद्ध करना पड़ता है।
इसकी बहुत ही सरल और एकल पंक्ति क्वेरी है।
SELECT * FROM Table_Name ORDER BY RAND() LIMIT 0,10;
order by rand()
यदि टेबल बड़ी है तो बहुत धीमी है
पुस्तक से:
एक ऑफसेट का उपयोग कर एक यादृच्छिक पंक्ति चुनें
अभी भी एक अन्य तकनीक जो पूर्ववर्ती विकल्पों में पाई गई समस्याओं से बचती है, वह है डेटा सेट में पंक्तियों को गिनना और 0 और गिनती के बीच एक यादृच्छिक संख्या वापस करना। डेटा सेट को क्वेरी करते समय इस नंबर का उपयोग ऑफसेट के रूप में करें
<?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();
इस समाधान का उपयोग तब करें जब आप सन्निहित महत्वपूर्ण मूल्यों को नहीं मान सकते हैं और आपको यह सुनिश्चित करने की आवश्यकता है कि प्रत्येक पंक्ति में चयनित होने का एक समान मौका है।
SELECT count(*)
धीमी हो जाती है।
तालिका से यादृच्छिक पंक्तियों का चयन कैसे करें:
यहाँ से: MySQL में यादृच्छिक पंक्तियों का चयन करें
रैंडम आईडी लेने के लिए इंडेक्स का उपयोग करने के लिए "टेबल स्कैन" पर एक त्वरित सुधार है।
SELECT *
FROM random, (
SELECT id AS sid
FROM random
ORDER BY RAND( )
LIMIT 10
) tmp
WHERE random.id = tmp.sid;
PRIMARY KEY
)।
यदि आपकी चाबी में कोई अंतराल नहीं है और वे सभी संख्यात्मक हैं तो आप यादृच्छिक संख्याओं की गणना कर सकते हैं और उन पंक्तियों का चयन कर सकते हैं। लेकिन यह शायद मामला नहीं होगा।
तो एक समाधान निम्नलिखित होगा:
SELECT * FROM table WHERE key >= FLOOR(RAND()*MAX(id)) LIMIT 1
जो मूल रूप से यह सुनिश्चित करेगा कि आपको अपनी कुंजियों की श्रेणी में एक यादृच्छिक संख्या मिल जाए और फिर आप अगले सर्वश्रेष्ठ का चयन करें जो अधिक से अधिक हो। आपको ऐसा 10 बार करना है।
हालाँकि यह वास्तव में यादृच्छिक नहीं है क्योंकि आपकी कुंजियाँ संभवतः समान रूप से वितरित नहीं की जाएंगी।
यह वास्तव में एक बड़ी समस्या है और सभी आवश्यकताओं को पूरा करने के लिए हल करना आसान नहीं है, MySQL की रैंड () सबसे अच्छी है जिसे आप प्राप्त कर सकते हैं यदि आप वास्तव में 10 यादृच्छिक पंक्तियां चाहते हैं।
हालांकि एक और उपाय है जो तेज है, लेकिन यादृच्छिकता की बात होने पर व्यापार बंद हो जाता है, लेकिन यह आपके लिए बेहतर हो सकता है। इसके बारे में यहां पढ़ें: मैं MySQL के ORDER BY RAND () फ़ंक्शन को कैसे अनुकूलित कर सकता हूं?
सवाल यह है कि आपको कितनी यादृच्छिकता की आवश्यकता है।
क्या आप थोड़ा और समझा सकते हैं ताकि मैं आपको एक अच्छा समाधान दे सकूं।
उदाहरण के लिए, जिस कंपनी के साथ मैंने काम किया था, उसके पास एक समाधान था जहां उन्हें बेहद यादृच्छिकता की आवश्यकता थी। उन्होंने डेटाबेस को उन यादृच्छिक मानों के साथ पूर्व-आबाद करने के साथ समाप्त किया जो कि अवरोही रूप से चुने गए थे और बाद में फिर से अलग-अलग यादृच्छिक मूल्यों पर सेट किए गए थे।
यदि आप शायद ही कभी अपडेट करते हैं, तो आप एक वृद्धि करने वाली आईडी भी भर सकते हैं ताकि आपके पास कोई अंतराल न हो और चयन करने से पहले यादृच्छिक कुंजियों की गणना कर सकें ... यह उपयोग के मामले पर निर्भर करता है!
Id
और आपके सभी यादृच्छिक प्रश्न आपको उस एक को वापस कर देंगे Id
।
FLOOR(RAND()*MAX(id))
बड़ा आईडी वापस करने के लिए पक्षपाती है।
मुझे एक बड़ी तालिका से बड़ी संख्या में यादृच्छिक पंक्तियों को वापस करने के लिए एक क्वेरी की आवश्यकता थी। मैंने ये ढूंढ निकाला। पहले अधिकतम रिकॉर्ड आईडी प्राप्त करें:
SELECT MAX(id) FROM table_name;
फिर उस मूल्य को इसमें प्रतिस्थापित करें:
SELECT * FROM table_name WHERE id > FLOOR(RAND() * max) LIMIT n;
जहां अधिकतम तालिका में अधिकतम रिकॉर्ड आईडी है और आपके परिणाम सेट में वांछित पंक्तियों की संख्या है। धारणा यह है कि रिकॉर्ड आईडी में कोई अंतराल नहीं है, हालांकि मुझे संदेह है कि यह परिणाम को प्रभावित करेगा अगर वहाँ थे (हालांकि यह कोशिश नहीं की है)। मैंने भी अधिक सामान्य होने के लिए इस संग्रहीत प्रक्रिया को बनाया; तालिका नाम और पास पंक्तियों की संख्या को वापस किया जाए। मैं Windows 2008, 32GB, दोहरे 3GHz E5450 पर MySQL 5.5.38 चला रहा हूं, और 17,361,264 पंक्तियों वाली एक मेज पर यह ~ .03 सेकंड / ~ 11 सेकंड में 1,000,000 पंक्तियों को वापस करने के लिए काफी सुसंगत है। (टाइम्स MySQL वर्कबेंच 6.1 से हैं; आप अपनी पसंद के आधार पर 2 के चयन स्टेटमेंट में FLOOR के बजाय CEIL का उपयोग कर सकते हैं)
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
$$
फिर
CALL [schema name].random_rows([table name], n);
मैंने @Riedsio के उत्तर में सुधार किया। यह सबसे कुशल क्वेरी है जिसे मैं अंतराल के साथ एक बड़े, समान रूप से वितरित तालिका पर पा सकता हूं (एक तालिका से 1000 यादृच्छिक पंक्तियों को प्राप्त करने पर परीक्षण किया गया है जिसमें> 2.6 बी पंक्तियां हैं)।
(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)
मुझे जाने क्या हो रहा है।
@max := (SELECT MAX(id) FROM table)
MAX(id)
प्रत्येक बार जब आपको एक पंक्ति की आवश्यकता होती है, तो गणना के लिए एक मामूली ओवरहेड होता हैSELECT FLOOR(rand() * @max) + 1 as rand)
SELECT id FROM table INNER JOIN (...) on id > rand LIMIT 1
संघ करना आपको 1 प्रश्न में सब कुछ फिट करने में मदद करता है ताकि आप कई प्रश्न करने से बच सकें। यह आपको गणना करने के ओवरहेड को बचाने की सुविधा भी देता हैMAX(id)
। आपके आवेदन के आधार पर, यह बहुत या बहुत कम हो सकता है।
ध्यान दें कि यह केवल आईडी प्राप्त करता है और उन्हें यादृच्छिक क्रम में प्राप्त करता है। यदि आप कुछ और उन्नत करना चाहते हैं तो मैं आपको यह करने की सलाह देता हूं:
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
करने के लिए LIMIT 30
क्वेरी में हर जगह
LIMIT 1
करने के लिए LIMIT 30
आप तालिका में एक यादृच्छिक बिंदु से एक पंक्ति में 30 रिकॉर्ड मिलेगा। आपको इसके बजाय (SELECT id FROM ....
बीच में भाग की 30 प्रतियां होनी चाहिए ।
Riedsio
उत्तर दें। मैंने पृष्ठ .2 पर and०० प्रति सेकंड हिट के साथ PHP 7.०२२ और मारियाबीडी का उपयोग कर 7 सेंट्स पर प्रयास किया है, Riedsio
उत्तर के साथ मुझे ५००+ अतिरिक्त सफल प्रतिक्रिया मिली फिर आपका उत्तर।
मैंने इस http://jan.kneschke.de/projects/mysql/order-by-rand/ को Riedsio द्वारा पोस्ट किया (मैंने एक संग्रहीत प्रक्रिया के मामले का उपयोग किया है जो एक या अधिक यादृच्छिक मान लौटाता है):
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;
लेख में वह आईडी में गैप की समस्या को हल करता है, जिससे टेबल बनाए रखने (ट्रिगर्स, आदि ... लेख देखें) का उपयोग करके इतने यादृच्छिक परिणाम नहीं होते हैं; मैं तालिका से एक और कॉलम जोड़कर समस्या को हल कर रहा हूं, 1 से शुरू होने वाली सन्निहित संख्याओं के साथ पॉपुलेट किया गया है ( संपादित करें: इस कॉलम को रनवे पर उपकुंजी द्वारा बनाई गई अस्थायी तालिका में जोड़ा जाता है, आपकी स्थायी तालिका को प्रभावित नहीं करता है):
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;
लेख में मैं देख सकता हूं कि वह कोड का अनुकूलन करने के लिए बहुत बड़ी लंबाई में गया था; मेरे पास कोई विचारधारा नहीं है / यदि मेरे परिवर्तन प्रदर्शन को प्रभावित करते हैं, लेकिन मेरे लिए बहुत अच्छा काम करता है।
@no_gaps_id
भी इंडेक्स का उपयोग नहीं किया जा सकता है, इसलिए यदि आप EXPLAIN
अपनी क्वेरी के लिए देखते हैं, तो आपके पास Using filesort
और Using where
(बिना इंडेक्स के) सबवेरीज़ के लिए, मूल क्वेरी के विपरीत है।
यहाँ एक गेम चेंजर है जो कई लोगों के लिए मददगार हो सकता है;
मेरे पास 200k पंक्तियों के साथ एक तालिका है, अनुक्रमिक आईडी के साथ , मुझे एन यादृच्छिक पंक्तियों को चुनने की आवश्यकता है , इसलिए मैं तालिका में सबसे बड़ी आईडी के आधार पर यादृच्छिक मान उत्पन्न करने का विकल्प चुनता हूं, मैंने यह जानने के लिए यह स्क्रिप्ट बनाई है कि सबसे तेज़ ऑपरेशन कौन सा है:
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();
परिणाम हैं:
36.8418693542479
एमएस0.241041183472
एमएस0.216960906982
एमएसइस परिणाम के आधार पर, ऑर्डर आईडी अधिकतम आईडी प्राप्त करने के लिए सबसे तेज़ ऑपरेशन है,
यहाँ प्रश्न का उत्तर है:
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 करें: 200k टेबल से 10 यादृच्छिक पंक्तियाँ प्राप्त करने के लिए, यह मुझे 1.78 ms (php साइड में सभी ऑपरेशन सहित) ले गया
LIMIT
थोड़ा बढ़ाएँ - आप डुप्लिकेट प्राप्त कर सकते हैं।
सभी सर्वोत्तम उत्तर पहले ही पोस्ट किए जा चुके हैं (मुख्यतः लिंक http://jan.kneschke.de/projects/mysql/order-by-rand/ को संदर्भित करने वाले )।
मैं एक और स्पीड-अप संभावना को इंगित करना चाहता हूं - कैशिंग । सोचें कि आपको यादृच्छिक पंक्तियों को प्राप्त करने की आवश्यकता क्यों है। संभवतः आप किसी वेबसाइट पर कुछ यादृच्छिक पोस्ट या यादृच्छिक विज्ञापन प्रदर्शित करना चाहते हैं। यदि आपको 100 req / s मिल रहे हैं, तो क्या वास्तव में यह आवश्यक है कि प्रत्येक आगंतुक को यादृच्छिक पंक्तियाँ मिलें? आमतौर पर 1 सेकंड (या 10 सेकंड) के लिए इन एक्स यादृच्छिक पंक्तियों को कैश करना पूरी तरह से ठीक है। इससे कोई फर्क नहीं पड़ता कि एक ही 1 सेकंड में 100 अनूठे आगंतुकों को समान यादृच्छिक पोस्ट मिलते हैं, क्योंकि अगले दूसरे 100 आगंतुकों को पोस्ट के अलग-अलग सेट मिलेंगे।
इस कैशिंग का उपयोग करते समय आप रैंडम डेटा प्राप्त करने के लिए कुछ धीमे समाधानों का भी उपयोग कर सकते हैं क्योंकि यह आपके req / s की परवाह किए बिना केवल एक बार MySQL से प्राप्त किया जाएगा।
यह सुपर फास्ट है और गैप होने पर भी 100% यादृच्छिक है।
x
आपके द्वारा उपलब्ध पंक्तियों की संख्या की गणना करेंSELECT COUNT(*) as rows FROM TABLE
a_1,a_2,...,a_10
0 और के बीच 10 अलग-अलग यादृच्छिक संख्याएँ चुनेंx
SELECT * FROM TABLE LIMIT 1 offset a_i
i = 1, ..., 10 के लिएमैं पुस्तक में इस हैक पाया एसक्यूएल Antipatterns से विधेयक Karwin ।
SELECT column FROM table ORDER BY RAND() LIMIT 10
O (nlog (n)) में है। तो हाँ, यह उपवास समाधान है और यह आईडी के किसी भी वितरण के लिए काम करता है।
x
। मैं तर्क दूंगा कि यह 10 पंक्तियों की यादृच्छिक पीढ़ी नहीं है। मेरे जवाब में, आपको चरण तीन में क्वेरी को 10 बार निष्पादित करना होगा, अर्थात प्रति निष्पादन केवल एक पंक्ति मिलती है और यदि तालिका के अंत में ऑफसेट है तो चिंता करने की ज़रूरत नहीं है।
एक टेम्प-टेबल के साथ @redsio के उत्तर को मिलाएं (600K इतना नहीं है):
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;
और फिर @redsios उत्तर का एक संस्करण लें:
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;
यदि तालिका बड़ी है, तो आप पहले भाग पर छलनी कर सकते हैं:
INSERT INTO tmp_randorder (data_id) select id from datatable where rand() < 0.01;
संस्करण: आप तालिका को tmp_randorder
निरंतर रख सकते हैं , इसे datatable_idlist कह सकते हैं। उस तालिका को कुछ अंतरालों (दिन, घंटे) में फिर से बनाएँ, क्योंकि उसमें भी छेद मिलेंगे। यदि आपकी तालिका वास्तव में बड़ी हो जाती है, तो आप छिद्रों को फिर से भरना भी कर सकते हैं
संपूर्ण ltata_id को datitable_idlist l से चुनें, जो dt.id = l.data_id पर datatable dt में शामिल हो, जहाँ dt.id शून्य है;
संस्करण: अपने डेटासेट को रैंडम_सोर्टऑर्डर कॉलम या तो सीधे डेटाटेबल में या लगातार अतिरिक्त तालिका में दें datatable_sortorder
। उस स्तंभ को अनुक्रमित करें। अपने एप्लिकेशन में एक रैंडम-मूल्य जेनरेट करें (मैं इसे कॉल करूंगा $rand
)।
select l.*
from datatable l
order by abs(random_sortorder - $rand) desc
limit 1;
यह समाधान उच्चतम और निम्नतम random_sortorder के साथ 'एज पंक्तियों' को विभेदित करता है, इसलिए उन्हें अंतराल में (दिन में एक बार) पुनर्व्यवस्थित करें।
एक और सरल समाधान पंक्तियों को क्रमबद्ध किया जाएगा और उनमें से एक को यादृच्छिक रूप से लाया जाएगा और इस समाधान के साथ आपको तालिका में किसी भी 'Id' आधारित कॉलम की आवश्यकता नहीं होगी।
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;
आप जितनी चाहे उतनी पंक्तियों का उपयोग करने के लिए अपनी सीमा के अनुसार सीमा मूल्य को बदल सकते हैं लेकिन यह ज्यादातर निरंतर मान होगा।
हालाँकि, यदि आप लगातार यादृच्छिक मूल्य नहीं चाहते हैं, तो आप एक बड़ा नमूना ला सकते हैं और उसमें से यादृच्छिक रूप से चयन कर सकते हैं। कुछ इस तरह ...
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;
एक तरीका है कि मैं बहुत अच्छा लगता है अगर वहाँ एक autogenerated आईडी modulo ऑपरेटर '%' का उपयोग करने के लिए है। उदाहरण के लिए, यदि आपको 70,000 में से 10,000 यादृच्छिक रिकॉर्ड की आवश्यकता है, तो आप यह कहकर इसे सरल बना सकते हैं कि आपको प्रत्येक 7 पंक्तियों में से 1 की आवश्यकता है। इसे इस क्वेरी में सरल बनाया जा सकता है:
SELECT * FROM
table
WHERE
id %
FLOOR(
(SELECT count(1) FROM table)
/ 10000
) = 0;
यदि कुल उपलब्ध टार्गेट पंक्तियों को विभाजित करने का परिणाम पूर्णांक नहीं है, तो आपके पास आपके द्वारा मांगे गए की तुलना में कुछ अतिरिक्त पंक्तियाँ होंगी, इसलिए आपको इस तरह सेट किए गए परिणाम को ट्रिम करने में मदद करने के लिए एक LIMIT क्लॉज जोड़ना चाहिए:
SELECT * FROM
table
WHERE
id %
FLOOR(
(SELECT count(1) FROM table)
/ 10000
) = 0
LIMIT 10000;
इसके लिए एक पूर्ण स्कैन की आवश्यकता होती है, लेकिन यह ORDER BY RAND की तुलना में तेज़ है, और मेरी राय में इस धागे में वर्णित अन्य विकल्पों की तुलना में समझने में सरल है। इसके अलावा अगर सिस्टम जो DB को लिखता है वह बैचों में पंक्तियों के सेट बनाता है तो आपको ऐसा यादृच्छिक परिणाम नहीं मिल सकता है जैसा कि आप उम्मीद कर रहे हैं।
यदि आप एक रैंडम रिकॉर्ड चाहते हैं (कोई फर्क नहीं पड़ता कि आईडी के बीच कोई अंतर है):
PREPARE stmt FROM 'SELECT * FROM `table_name` LIMIT 1 OFFSET ?';
SET @count = (SELECT
FLOOR(RAND() * COUNT(*))
FROM `table_name`);
EXECUTE stmt USING @count;
मैंने सभी उत्तरों को देखा है, और मुझे नहीं लगता कि किसी ने भी इस संभावना का उल्लेख किया है, और मुझे यकीन नहीं है कि क्यों।
यदि आप मामूली लागत पर अत्यंत सरलता और गति चाहते हैं, तो मेरे लिए यह डीबी में प्रत्येक पंक्ति के खिलाफ एक यादृच्छिक संख्या को संग्रहीत करने के लिए समझ में आता है। बस एक अतिरिक्त कॉलम बनाएं random_number
, और इसे डिफ़ॉल्ट रूप से सेट करें RAND()
। इस कॉलम पर एक इंडेक्स बनाएं।
फिर जब आप एक पंक्ति को पुनः प्राप्त करना चाहते हैं तो अपने कोड (PHP, पर्ल, जो भी हो) में एक यादृच्छिक संख्या उत्पन्न करें और उस कॉलम से तुलना करें।
SELECT FROM tbl WHERE random_number >= :random LIMIT 1
मुझे लगता है कि यह एक पंक्ति के लिए बहुत साफ है, दस पंक्तियों के लिए जैसे कि ओपी ने पूछा था कि आपको इसे दस अलग-अलग समयों पर कॉल करना होगा (या एक चतुर ट्वीक के साथ आना होगा जो मुझे तुरंत बचता है)
निम्नलिखित कॉलम के तेज, निष्पक्ष और स्वतंत्र होना चाहिए। हालाँकि यह गारंटी नहीं देता है कि दी गई पंक्तियों की संख्या अनुरोधित पंक्तियों की संख्या से मेल खाएगी।
SELECT *
FROM t
WHERE RAND() < (SELECT 10 / COUNT(*) FROM t)
स्पष्टीकरण: मान लें कि आप 100 में से 10 पंक्तियाँ चाहते हैं, तो प्रत्येक पंक्ति में चयनित होने की 1/10 संभावना है जिसे प्राप्त किया जा सकता है WHERE RAND() < 0.1
। यह दृष्टिकोण 10 पंक्तियों की गारंटी नहीं देता है; लेकिन यदि क्वेरी को पर्याप्त बार चलाया जाता है तो प्रति निष्पादन पंक्तियों की औसत संख्या लगभग 10 होगी और तालिका में प्रत्येक पंक्ति समान रूप से चुनी जाएगी।
PREPARE stm from 'select * from table limit 10 offset ?';
SET @total = (select count(*) from table);
SET @_offset = FLOOR(RAND() * @total);
EXECUTE stm using @_offset;
आप एक क्लॉज भी लागू कर सकते हैं जैसे
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 पंक्तियों (700MB) तालिका क्वेरी निष्पादन पर परीक्षण किया ~ 0.016sec HDD ड्राइव
--EDIT--
ऑफसेट तालिका के अंत के करीब एक मूल्य ले सकता है, जिसके परिणामस्वरूप चयन कम पंक्तियों (या शायद केवल 1) का चयन करेगा पंक्ति), इससे बचने के लिए हम offset
इसे घोषित करने के बाद फिर से जाँच कर सकते हैं , जैसे
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;
मैं इस क्वेरी का उपयोग करता हूं:
select floor(RAND() * (SELECT MAX(key) FROM table)) from table limit 10
क्वेरी समय: 0.016 s
यह मेरा इसे करने का तरीका है:
select *
from table_with_600k_rows
where rand() < 10/600000
limit 10
मुझे यह पसंद है क्योंकि अन्य तालिकाओं की आवश्यकता नहीं है, यह लिखना सरल है, और इसे निष्पादित करने के लिए बहुत तेज़ है।
तालिका से यादृच्छिक डेटा प्राप्त करने के लिए नीचे सरल क्वेरी का उपयोग करें।
SELECT user_firstname ,
COUNT(DISTINCT usr_fk_id) cnt
FROM userdetails
GROUP BY usr_fk_id
ORDER BY cnt ASC
LIMIT 10
मुझे लगता है कि यह सबसे अच्छा तरीका है।
SELECT id, id * RAND( ) AS random_no, first_name, last_name
FROM user
ORDER BY random_no