MySQL 600K पंक्तियों से 10 यादृच्छिक पंक्तियों को तेजी से चुनता है


463

मैं सर्वश्रेष्ठ क्वेरी कैसे लिख सकता हूं जो कुल 600k में से 10 पंक्तियों को यादृच्छिक रूप से चुनता है?


15
यहां 8 तकनीकें हैं ; शायद कोई आपके मामले में अच्छा काम करेगा।
रिक जेम्स

जवाबों:


386

साधारण से लेकर अंतराल तक, गैप के साथ गैर-समान रूप से कई मामलों को संभालने वाला एक शानदार पोस्ट।

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

यह मानता है कि आईडी का वितरण बराबर है, और आईडी सूची में अंतराल हो सकता है। अधिक उन्नत उदाहरणों के लिए लेख देखें


52
हां, यदि आपके पास आईडी में संभावित रूप से बड़े अंतराल हैं तो आपकी सबसे कम आईडी के बेतरतीब ढंग से उठाए जाने की संभावना आपकी उच्च आईडी की तुलना में बहुत कम है। वास्तव में मौका है कि सबसे बड़ी खाई के बाद पहली आईडी वास्तव में उच्चतम है। इसलिए यह परिभाषा से यादृच्छिक नहीं है।
ल्यूकोकोड्स

6
आप 10 अलग-अलग यादृच्छिक पंक्तियाँ कैसे प्राप्त करेंगे? क्या आपको 10 की सीमा तय करनी है और फिर 10 बार पुनरावृति करनी है mysqli_fetch_assoc($result)? या क्या वे 10 परिणाम आवश्यक नहीं हैं?
एडम

12
मेरे दिमाग में किसी भी परिणाम के लिए रैंडम को समान अवसर की आवश्यकता होती है। ;)
ल्यूकोकोड्स

4
पूरा लेख असमान वितरण और बार-बार परिणाम जैसे मुद्दों को संबोधित करता है।
ब्रैड सजोनी

1
विशेष रूप से, यदि आपके पास अपनी आईडी की शुरुआत में अंतराल है, तो पहले वाला समय का उठाया (न्यूनतम / अधिकतम-मिनट) मिलेगा। उस स्थिति के लिए एक साधारण ट्वीक है MAX () - MIN () * RAND + MIN (), जो बहुत धीमा नहीं है।
कोड एबोमिनेटर

342
SELECT column FROM table
ORDER BY RAND()
LIMIT 10

कुशल समाधान नहीं है लेकिन काम करता है


139
ORDER BY RAND()अपेक्षाकृत धीमी गति से है
Mateusz Charytoniuk

7
Mateusz - प्रूफ pls, SELECT words, transcription, translation, sound FROM vocabulary WHERE menu_id=$menuId ORDER BY RAND() LIMIT 100.0010 लेता है, बिना LIMIT 10 यह 0.0012 लिया (उस तालिका में 3500 शब्द)।
आर्थर कुशमैन

26
@zeusakm 3500 शब्द इतना अधिक नहीं है; समस्या यह है कि यह एक निश्चित बिंदु से आगे निकल जाता है क्योंकि MySQL को वास्तव में हर एक को पढ़ने के बाद सभी रिकॉर्ड को सॉर्ट करना पड़ता है; एक बार जब ऑपरेशन कठिन डिस्क को हिट करता है तो आप अंतर महसूस कर सकते हैं।
जाकोक

16
मैं अपने आप को दोहराना नहीं चाहता, लेकिन फिर से, यह पूर्ण टेबल स्कैन है। बड़ी मेज पर यह बहुत समय और मेमोरी खपत है और डिस्क पर अस्थायी टेबल पर & ऑपरेशन के निर्माण का कारण हो सकता है जो बहुत धीमा है।
मैट

10
जब मैं 2010 में फेसबुक के साथ साक्षात्कार कर रहा था, तो उन्होंने मुझसे पूछा कि एक पढ़ने में, अज्ञात आकार की एक बड़ी फ़ाइल से यादृच्छिक रिकॉर्ड का चयन कैसे करें। एक बार जब आप एक विचार के साथ आते हैं, तो कई रिकॉर्डों के चयन के लिए इसे सामान्य करना आसान होता है। तो हां, पूरी फाइल को छांटना हास्यास्पद है। उसी समय, यह बहुत आसान है। मैंने सिर्फ 1,000,000 + पंक्तियों वाली तालिका से 10 यादृच्छिक पंक्तियों को लेने के लिए इस दृष्टिकोण का उपयोग किया। यकीन है, मुझे थोड़ा इंतजार करना पड़ा; लेकिन मैं सिर्फ एक विचार, लगता है क्या ठेठ पंक्तियों इस तालिका में प्राप्त करना चाहता था ...
ओएसए

27

सरल क्वेरी जिसमें उत्कृष्ट प्रदर्शन होता है और अंतराल के साथ काम करता है :

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


1
क्षमा करें, मैंने परीक्षण किया! 600k रिकॉर्ड पर धीमी गति से प्रदर्शन।
डायलन बी

@DylanB I ने परीक्षण के साथ उत्तर को अपडेट किया।
अली

17

मैं धीमी सीपीयू के साथ तेजी से क्वेरी (लगभग 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);
?>

11
मेरे से अधिक 14 लाख रिकॉर्ड तालिका को देखते हुए इसे धीमा के रूप में के रूप में हैORDER BY RAND()
फैब्रिजियो

5
@snippetsofcode आपके मामले में - 400k पंक्तियों में आप सरल "ORDER BY रैंड ()" का उपयोग कर सकते हैं। 3 प्रश्नों के साथ आपकी चाल बेकार है। आप इसे फिर से लिख सकते हैं जैसे "SELECT id, url
from Pages

4
आपकी तकनीक अभी भी एक टेबल स्कैन करती है। FLUSH STATUS; SELECT ...; SHOW SESSION STATUS LIKE 'Handler%';इसे देखने के लिए उपयोग करें ।
रिक जेम्स

4
उस क्वेरी को 200 req / s वेबपेज में चलाने का भी प्रयास करें। कंसीलर आपको मार देगा।
Marki555

सादे ORDER BY RAND()से अधिक इस @ रमनप्लोडिनोव का लाभ यह है कि यह केवल आईडी (पूर्ण पंक्तियों नहीं) को छांटता है , इसलिए अस्थायी तालिका छोटी होती है, लेकिन फिर भी उन सभी को क्रमबद्ध करना पड़ता है।
मार्की ५५५

16

इसकी बहुत ही सरल और एकल पंक्ति क्वेरी है।

SELECT * FROM Table_Name ORDER BY RAND() LIMIT 0,10;

20
FYI करें, order by rand()यदि टेबल बड़ी है तो बहुत धीमी है
badReiko

6
कभी-कभी SLOW को स्वीकार कर लिया जाता है यदि मैं इसे SIMPLE रखना चाहता

यदि इसके बड़े होने पर तालिका पर अनुक्रमण लागू किया जाना चाहिए।
मुहम्मद अज़ीम

1
अनुक्रमण यहाँ मदद नहीं करेगा। इंडेक्स बहुत विशिष्ट चीजों के लिए सहायक होते हैं, और यह क्वेरी उनमें से एक नहीं है।
एंड्रयू

13

पुस्तक से:

एक ऑफसेट का उपयोग कर एक यादृच्छिक पंक्ति चुनें

अभी भी एक अन्य तकनीक जो पूर्ववर्ती विकल्पों में पाई गई समस्याओं से बचती है, वह है डेटा सेट में पंक्तियों को गिनना और 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();

इस समाधान का उपयोग तब करें जब आप सन्निहित महत्वपूर्ण मूल्यों को नहीं मान सकते हैं और आपको यह सुनिश्चित करने की आवश्यकता है कि प्रत्येक पंक्ति में चयनित होने का एक समान मौका है।


1
बहुत बड़ी तालिकाओं के लिए, SELECT count(*)धीमी हो जाती है।
हंस जेड २

7

तालिका से यादृच्छिक पंक्तियों का चयन कैसे करें:

यहाँ से: MySQL में यादृच्छिक पंक्तियों का चयन करें

रैंडम आईडी लेने के लिए इंडेक्स का उपयोग करने के लिए "टेबल स्कैन" पर एक त्वरित सुधार है।

SELECT *
FROM random, (
        SELECT id AS sid
        FROM random
        ORDER BY RAND( )
        LIMIT 10
    ) tmp
WHERE random.id = tmp.sid;

1
यह MyISAM के लिए कुछ मदद करता है, लेकिन InnoDB के लिए नहीं (यह मानते हुए आईडी क्लस्टर है PRIMARY KEY)।
रिक जेम्स

7

यदि आपकी चाबी में कोई अंतराल नहीं है और वे सभी संख्यात्मक हैं तो आप यादृच्छिक संख्याओं की गणना कर सकते हैं और उन पंक्तियों का चयन कर सकते हैं। लेकिन यह शायद मामला नहीं होगा।

तो एक समाधान निम्नलिखित होगा:

SELECT * FROM table WHERE key >= FLOOR(RAND()*MAX(id)) LIMIT 1

जो मूल रूप से यह सुनिश्चित करेगा कि आपको अपनी कुंजियों की श्रेणी में एक यादृच्छिक संख्या मिल जाए और फिर आप अगले सर्वश्रेष्ठ का चयन करें जो अधिक से अधिक हो। आपको ऐसा 10 बार करना है।

हालाँकि यह वास्तव में यादृच्छिक नहीं है क्योंकि आपकी कुंजियाँ संभवतः समान रूप से वितरित नहीं की जाएंगी।

यह वास्तव में एक बड़ी समस्या है और सभी आवश्यकताओं को पूरा करने के लिए हल करना आसान नहीं है, MySQL की रैंड () सबसे अच्छी है जिसे आप प्राप्त कर सकते हैं यदि आप वास्तव में 10 यादृच्छिक पंक्तियां चाहते हैं।

हालांकि एक और उपाय है जो तेज है, लेकिन यादृच्छिकता की बात होने पर व्यापार बंद हो जाता है, लेकिन यह आपके लिए बेहतर हो सकता है। इसके बारे में यहां पढ़ें: मैं MySQL के ORDER BY RAND () फ़ंक्शन को कैसे अनुकूलित कर सकता हूं?

सवाल यह है कि आपको कितनी यादृच्छिकता की आवश्यकता है।

क्या आप थोड़ा और समझा सकते हैं ताकि मैं आपको एक अच्छा समाधान दे सकूं।

उदाहरण के लिए, जिस कंपनी के साथ मैंने काम किया था, उसके पास एक समाधान था जहां उन्हें बेहद यादृच्छिकता की आवश्यकता थी। उन्होंने डेटाबेस को उन यादृच्छिक मानों के साथ पूर्व-आबाद करने के साथ समाप्त किया जो कि अवरोही रूप से चुने गए थे और बाद में फिर से अलग-अलग यादृच्छिक मूल्यों पर सेट किए गए थे।

यदि आप शायद ही कभी अपडेट करते हैं, तो आप एक वृद्धि करने वाली आईडी भी भर सकते हैं ताकि आपके पास कोई अंतराल न हो और चयन करने से पहले यादृच्छिक कुंजियों की गणना कर सकें ... यह उपयोग के मामले पर निर्भर करता है!


हाय जो। इस विशेष मामले में कुंजियों में अंतराल की कमी नहीं होनी चाहिए, लेकिन समय के साथ यह बदल सकती है। और जब आपका उत्तर काम करता है, तो यह यादृच्छिक 10 पंक्तियों को उत्पन्न करेगा (बशर्ते मैं 10 की सीमा लिखता हूं) जो लगातार हैं और मुझे बोलने के लिए अधिक यादृच्छिकता चाहिए थी। :) धन्यवाद।
फ्रांसिस्क

यदि आपको 10 विशिष्ट पंक्तियों को उत्पन्न करने के लिए किसी प्रकार के संघ के 10 उपयोग की आवश्यकता है।
जॉनो डे

मैंने क्या कहा था। आपको उस 10 बार निष्पादित करने की आवश्यकता है। इसे यूनियन में मिलाना एक तरह से इसे एक क्वेरी में रखना है। मेरे परिशिष्ट 2 मिनट पहले देखें।
22

1
@ तूफान, यह समाधान शांत दिखता है, लेकिन अत्यधिक त्रुटिपूर्ण है । केवल एक बहुत बड़ा सम्मिलित करने का प्रयास करें Idऔर आपके सभी यादृच्छिक प्रश्न आपको उस एक को वापस कर देंगे Id
पैशियर

1
FLOOR(RAND()*MAX(id))बड़ा आईडी वापस करने के लिए पक्षपाती है।
रिक जेम्स

3

मुझे एक बड़ी तालिका से बड़ी संख्या में यादृच्छिक पंक्तियों को वापस करने के लिए एक क्वेरी की आवश्यकता थी। मैंने ये ढूंढ निकाला। पहले अधिकतम रिकॉर्ड आईडी प्राप्त करें:

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);

3

मैंने @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)

मुझे जाने क्या हो रहा है।

  1. @max := (SELECT MAX(id) FROM table)
    • मैं गणना कर रहा हूं और अधिकतम बचत कर रहा हूं। बहुत बड़ी तालिकाओं के लिए, MAX(id)प्रत्येक बार जब आपको एक पंक्ति की आवश्यकता होती है, तो गणना के लिए एक मामूली ओवरहेड होता है
  2. SELECT FLOOR(rand() * @max) + 1 as rand)
    • एक यादृच्छिक आईडी हो जाता है
  3. 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

मैं 30 यादृच्छिक रिकॉर्ड की जरूरत है, तो मैं बदलना चाहिए LIMIT 1करने के लिए LIMIT 30क्वेरी में हर जगह
Hassaan

@Hassaan आप नहीं करना चाहिए, जो बदल LIMIT 1करने के लिए LIMIT 30आप तालिका में एक यादृच्छिक बिंदु से एक पंक्ति में 30 रिकॉर्ड मिलेगा। आपको इसके बजाय (SELECT id FROM ....बीच में भाग की 30 प्रतियां होनी चाहिए ।
हंस जेड

मैंने कोशिश की है लेकिन अधिक कुशल नहीं लगता है तो Riedsioउत्तर दें। मैंने पृष्ठ .2 पर and०० प्रति सेकंड हिट के साथ PHP 7.०२२ और मारियाबीडी का उपयोग कर 7 सेंट्स पर प्रयास किया है, Riedsioउत्तर के साथ मुझे ५००+ अतिरिक्त सफल प्रतिक्रिया मिली फिर आपका उत्तर।
हस्सान

1
@ हसन राइड्सियो का जवाब 1 पंक्ति देता है, यह आपको n पंक्तियाँ देता है, साथ ही क्वेरी करने के लिए I / O ओवरहेड पर कटौती करता है। आप पंक्तियों को तेज़ी से प्राप्त करने में सक्षम हो सकते हैं, लेकिन आपके सिस्टम पर अधिक लोड के साथ।
हंस Z

3

मैंने इस 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(बिना इंडेक्स के) सबवेरीज़ के लिए, मूल क्वेरी के विपरीत है।
फेबियन शेंगलर

2

यहाँ एक गेम चेंजर है जो कई लोगों के लिए मददगार हो सकता है;

मेरे पास 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 साइड में सभी ऑपरेशन सहित) ले गया


3
सुझाव दें कि आप LIMITथोड़ा बढ़ाएँ - आप डुप्लिकेट प्राप्त कर सकते हैं।
रिक जेम्स

2

सभी सर्वोत्तम उत्तर पहले ही पोस्ट किए जा चुके हैं (मुख्यतः लिंक http://jan.kneschke.de/projects/mysql/order-by-rand/ को संदर्भित करने वाले )।

मैं एक और स्पीड-अप संभावना को इंगित करना चाहता हूं - कैशिंग । सोचें कि आपको यादृच्छिक पंक्तियों को प्राप्त करने की आवश्यकता क्यों है। संभवतः आप किसी वेबसाइट पर कुछ यादृच्छिक पोस्ट या यादृच्छिक विज्ञापन प्रदर्शित करना चाहते हैं। यदि आपको 100 req / s मिल रहे हैं, तो क्या वास्तव में यह आवश्यक है कि प्रत्येक आगंतुक को यादृच्छिक पंक्तियाँ मिलें? आमतौर पर 1 सेकंड (या 10 सेकंड) के लिए इन एक्स यादृच्छिक पंक्तियों को कैश करना पूरी तरह से ठीक है। इससे कोई फर्क नहीं पड़ता कि एक ही 1 सेकंड में 100 अनूठे आगंतुकों को समान यादृच्छिक पोस्ट मिलते हैं, क्योंकि अगले दूसरे 100 आगंतुकों को पोस्ट के अलग-अलग सेट मिलेंगे।

इस कैशिंग का उपयोग करते समय आप रैंडम डेटा प्राप्त करने के लिए कुछ धीमे समाधानों का भी उपयोग कर सकते हैं क्योंकि यह आपके req / s की परवाह किए बिना केवल एक बार MySQL से प्राप्त किया जाएगा।


2

यह सुपर फास्ट है और गैप होने पर भी 100% यादृच्छिक है।

  1. xआपके द्वारा उपलब्ध पंक्तियों की संख्या की गणना करेंSELECT COUNT(*) as rows FROM TABLE
  2. a_1,a_2,...,a_100 और के बीच 10 अलग-अलग यादृच्छिक संख्याएँ चुनेंx
  3. अपनी पंक्तियों को इस तरह से छोड़ें: SELECT * FROM TABLE LIMIT 1 offset a_ii = 1, ..., 10 के लिए

मैं पुस्तक में इस हैक पाया एसक्यूएल Antipatterns से विधेयक Karwin


मैं उसी उपाय के बारे में सोच रहा था, कृपया मुझे बताएं, क्या यह तेजी से है तो अन्य विधि?
जी। अदनान

@ G.Adnane इसका त्वरित या धीमा नहीं है तो स्वीकृत उत्तर है, लेकिन स्वीकृत उत्तर आईडी के समान वितरण को मानता है। मैं किसी भी परिदृश्य की कल्पना नहीं कर सकता जहाँ इसकी गारंटी हो। यह समाधान O (1) में है जहां समाधान SELECT column FROM table ORDER BY RAND() LIMIT 10O (nlog (n)) में है। तो हाँ, यह उपवास समाधान है और यह आईडी के किसी भी वितरण के लिए काम करता है।
एडम

नहीं, क्योंकि स्वीकृत समाधान के लिए पोस्ट किए गए लिंक में, अन्य विधियां हैं, मैं जानना चाहता हूं कि क्या यह समाधान तेज है तो अन्य, अन्य तरीके, हम एक और खोजने की कोशिश कर सकते हैं, यही कारण है कि Iam पूछ रहा है, किसी भी तरह से, +1 तुम्हारे जवाब के लिए। मैं
जी

एक ऐसा मामला है जब आप x संख्या पंक्तियों को प्राप्त करना चाहते हैं, लेकिन ऑफ़सेट तालिका के अंत में जाता है जो <x पंक्तियों या 1 पंक्ति में वापस आ जाएगी। मैंने अपना पोस्ट देखने से पहले आपका जवाब नहीं देखा, लेकिन मैंने इसे और अधिक स्पष्ट कर दिया है stackoverflow.com/a/59981772/10387008
ZOLDIK

@ZOLDIK ऐसा लगता है कि आप ऑफसेट के बाद पहली 10 पंक्तियाँ चुनते हैं x। मैं तर्क दूंगा कि यह 10 पंक्तियों की यादृच्छिक पीढ़ी नहीं है। मेरे जवाब में, आपको चरण तीन में क्वेरी को 10 बार निष्पादित करना होगा, अर्थात प्रति निष्पादन केवल एक पंक्ति मिलती है और यदि तालिका के अंत में ऑफसेट है तो चिंता करने की ज़रूरत नहीं है।
एडम

1

अगर आपके पास सिर्फ एक Read-Request है

एक टेम्प-टेबल के साथ @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;

यदि आपके पास कई रीड-रिक्वेस्ट हैं

  1. संस्करण: आप तालिका को tmp_randorderनिरंतर रख सकते हैं , इसे datatable_idlist कह सकते हैं। उस तालिका को कुछ अंतरालों (दिन, घंटे) में फिर से बनाएँ, क्योंकि उसमें भी छेद मिलेंगे। यदि आपकी तालिका वास्तव में बड़ी हो जाती है, तो आप छिद्रों को फिर से भरना भी कर सकते हैं

    संपूर्ण ltata_id को datitable_idlist l से चुनें, जो dt.id = l.data_id पर datatable dt में शामिल हो, जहाँ dt.id शून्य है;

  2. संस्करण: अपने डेटासेट को रैंडम_सोर्टऑर्डर कॉलम या तो सीधे डेटाटेबल में या लगातार अतिरिक्त तालिका में दें datatable_sortorder। उस स्तंभ को अनुक्रमित करें। अपने एप्लिकेशन में एक रैंडम-मूल्य जेनरेट करें (मैं इसे कॉल करूंगा $rand)।

    select l.*
    from datatable l 
    order by abs(random_sortorder - $rand) desc 
    limit 1;

यह समाधान उच्चतम और निम्नतम random_sortorder के साथ 'एज पंक्तियों' को विभेदित करता है, इसलिए उन्हें अंतराल में (दिन में एक बार) पुनर्व्यवस्थित करें।


1

एक और सरल समाधान पंक्तियों को क्रमबद्ध किया जाएगा और उनमें से एक को यादृच्छिक रूप से लाया जाएगा और इस समाधान के साथ आपको तालिका में किसी भी '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;

1

एक तरीका है कि मैं बहुत अच्छा लगता है अगर वहाँ एक 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 को लिखता है वह बैचों में पंक्तियों के सेट बनाता है तो आपको ऐसा यादृच्छिक परिणाम नहीं मिल सकता है जैसा कि आप उम्मीद कर रहे हैं।


2
अब जब मुझे ऐसा लगता है, अगर आपको इसे कॉल करने के लिए हर बार यादृच्छिक पंक्तियों की आवश्यकता है, तो यह बेकार है। मैं केवल कुछ शोध करने के लिए एक सेट से यादृच्छिक पंक्तियों को प्राप्त करने की आवश्यकता के बारे में सोच रहा था। मुझे अभी भी लगता है कि दूसरे मामले में मदद करने के लिए मोडुलो एक अच्छी बात है। आप ORDER BY RAND ऑपरेशन की लागत को कम करने के लिए पहली पास फिल्टर के रूप में modulo का उपयोग कर सकते हैं।
निकोलस कोहेन

1

यदि आप एक रैंडम रिकॉर्ड चाहते हैं (कोई फर्क नहीं पड़ता कि आईडी के बीच कोई अंतर है):

PREPARE stmt FROM 'SELECT * FROM `table_name` LIMIT 1 OFFSET ?';
SET @count = (SELECT
        FLOOR(RAND() * COUNT(*))
    FROM `table_name`);

EXECUTE stmt USING @count;

स्रोत: https://www.warpconduit.net/2011/03/23/selecting-a-random-record-use-mysql-benchmark-results/#comment-1266


1

मैंने सभी उत्तरों को देखा है, और मुझे नहीं लगता कि किसी ने भी इस संभावना का उल्लेख किया है, और मुझे यकीन नहीं है कि क्यों।

यदि आप मामूली लागत पर अत्यंत सरलता और गति चाहते हैं, तो मेरे लिए यह डीबी में प्रत्येक पंक्ति के खिलाफ एक यादृच्छिक संख्या को संग्रहीत करने के लिए समझ में आता है। बस एक अतिरिक्त कॉलम बनाएं random_number, और इसे डिफ़ॉल्ट रूप से सेट करें RAND()। इस कॉलम पर एक इंडेक्स बनाएं।

फिर जब आप एक पंक्ति को पुनः प्राप्त करना चाहते हैं तो अपने कोड (PHP, पर्ल, जो भी हो) में एक यादृच्छिक संख्या उत्पन्न करें और उस कॉलम से तुलना करें।

SELECT FROM tbl WHERE random_number >= :random LIMIT 1

मुझे लगता है कि यह एक पंक्ति के लिए बहुत साफ है, दस पंक्तियों के लिए जैसे कि ओपी ने पूछा था कि आपको इसे दस अलग-अलग समयों पर कॉल करना होगा (या एक चतुर ट्वीक के साथ आना होगा जो मुझे तुरंत बचता है)


यह वास्तव में एक बहुत अच्छा और कुशल दृष्टिकोण है। एकमात्र वापसी यह तथ्य है कि आपने गति के लिए स्थान का कारोबार किया, जो मेरी राय में एक उचित सौदा जैसा लगता है।
तपुकुवु नक्मदिलिम

धन्यवाद। मेरे पास एक ऐसा परिदृश्य था जहां मुख्य तालिका से मैं एक यादृच्छिक पंक्ति चाहता था जिसमें 5 मिलियन पंक्तियाँ थीं, और बहुत सारे जोड़ थे, और इस प्रश्न में अधिकांश दृष्टिकोणों की कोशिश करने के बाद यह वह कीचड़ था जिस पर मैं बस गया था। एक अतिरिक्त स्तंभ मेरे लिए एक बहुत ही उपयोगी था।
कोडमेकिन

0

निम्नलिखित कॉलम के तेज, निष्पक्ष और स्वतंत्र होना चाहिए। हालाँकि यह गारंटी नहीं देता है कि दी गई पंक्तियों की संख्या अनुरोधित पंक्तियों की संख्या से मेल खाएगी।

SELECT *
FROM t
WHERE RAND() < (SELECT 10 / COUNT(*) FROM t)

स्पष्टीकरण: मान लें कि आप 100 में से 10 पंक्तियाँ चाहते हैं, तो प्रत्येक पंक्ति में चयनित होने की 1/10 संभावना है जिसे प्राप्त किया जा सकता है WHERE RAND() < 0.1। यह दृष्टिकोण 10 पंक्तियों की गारंटी नहीं देता है; लेकिन यदि क्वेरी को पर्याप्त बार चलाया जाता है तो प्रति निष्पादन पंक्तियों की औसत संख्या लगभग 10 होगी और तालिका में प्रत्येक पंक्ति समान रूप से चुनी जाएगी।


0

आप आसानी से एक सीमा के साथ एक यादृच्छिक ऑफसेट का उपयोग कर सकते हैं

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;

-1

मैं इस क्वेरी का उपयोग करता हूं:

select floor(RAND() * (SELECT MAX(key) FROM table)) from table limit 10

क्वेरी समय: 0.016 s


पीके जैसे 1,2,9,15 है। क्वेरी के ऊपर आपको 4, 7, 14, 11 जैसी पंक्तियाँ मिलेंगी जो अपर्याप्त हैं!
जुनैद अटारी

-2

यह मेरा इसे करने का तरीका है:

select * 
from table_with_600k_rows
where rand() < 10/600000
limit 10

मुझे यह पसंद है क्योंकि अन्य तालिकाओं की आवश्यकता नहीं है, यह लिखना सरल है, और इसे निष्पादित करने के लिए बहुत तेज़ है।


5
यह फुल टेबल स्कैन है और यह किसी भी इंडेक्स का उपयोग नहीं करता है। बड़े तालिकाओं और व्यस्त वातावरण के लिए यह बड़ा नहीं नहीं है।
मैट

-2

तालिका से यादृच्छिक डेटा प्राप्त करने के लिए नीचे सरल क्वेरी का उपयोग करें।

SELECT user_firstname ,
COUNT(DISTINCT usr_fk_id) cnt
FROM userdetails 
GROUP BY usr_fk_id 
ORDER BY cnt ASC  
LIMIT 10

यदि आप किसी भी जॉइन स्टेटमेंट का उपयोग करना चाहते हैं और आप कहां फ़िल्टर का उपयोग कर सकते हैं।
MANOJ

3
क्वेरी के किस भाग से आपको यादृच्छिक-नेस मिलता है?
मार्की 555

-4

मुझे लगता है कि यह सबसे अच्छा तरीका है।

SELECT id, id * RAND( ) AS random_no, first_name, last_name
FROM user
ORDER BY random_no

8
नर्क नहीं, यह टेबल से यादृच्छिक पंक्तियाँ प्राप्त करने के सबसे बुरे तरीकों में से एक है। यह फुल टेबल स्कैन + फाइलस्पोर्ट + टैम्प टेबल = खराब प्रदर्शन है।
मैट

1
प्रदर्शन के अलावा, यह भी पूरी तरह से यादृच्छिक से दूर है; आप केवल एक यादृच्छिक संख्या द्वारा आदेश देने के बजाय आईडी और एक यादृच्छिक संख्या के उत्पाद द्वारा आदेश दे रहे हैं, जिसका अर्थ है कि निम्न आईडी वाली पंक्तियां आपके परिणामों के सेट में पहले दिखाई देने के पक्षपाती होने वाली हैं।
मार्क एमी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.