एक sqlite तालिका से यादृच्छिक पंक्ति का चयन करें


119

मेरे पास sqliteनिम्नलिखित स्कीमा के साथ एक तालिका है:

CREATE TABLE foo (bar VARCHAR)

मैं इस तालिका को स्ट्रिंग की सूची के लिए भंडारण के रूप में उपयोग कर रहा हूं।

मैं इस तालिका से एक यादृच्छिक पंक्ति का चयन कैसे करूं?


जवाबों:


213

SQLite तालिका से रैंडम पंक्ति का चयन करने पर एक नज़र है

SELECT * FROM table ORDER BY RANDOM() LIMIT 1;

1
इस समाधान को किसी ज्वाइन में कैसे बढ़ाया जाए? उपयोग करते समय SELECT a.foo FROM a JOIN b ON a.id = b.id WHERE b.bar = 2 ORDER BY RANDOM() LIMIT 1;मुझे हमेशा एक ही पंक्ति मिलती है।
हेल्मुट ग्रोने

क्या यादृच्छिक संख्या को बीज करना संभव है। उदाहरण के लिए, बुक ऑफ द डे को आज के लिए यूनिक्स एपोक के साथ दोपहर में रखा गया है, इसलिए यह पूरे दिन एक ही किताब दिखाता है, भले ही क्वेरी कई बार चल रही हो। हाँ मुझे पता है कि कैशिंग इस उपयोग के मामले में सिर्फ एक उदाहरण के लिए अधिक कुशल है।
डेनियलसन 317

एफडब्ल्यूआईडब्ल्यू मेरा प्रश्न वास्तव में यहां उत्तर दिया गया है। और जवाब है कि आप रैंडम नंबर को सीड नहीं कर सकते। stackoverflow.com/questions/24256258/…
danielson317

31

निम्नलिखित समाधान एक्टास्टिक की तुलना में बहुत तेज हैं (गिनती (*) में बहुत खर्च होता है, लेकिन यदि आप इसे कैश कर सकते हैं, तो अंतर इतना बड़ा नहीं होना चाहिए), जो खुद "यादृच्छिक द्वारा क्रम" () से बहुत तेज है। जब आपके पास बड़ी संख्या में पंक्तियाँ हों, हालाँकि उनके पास कुछ असुविधाएँ हैं।

यदि आपकी राइडिड्स भरी हुई हैं (यानी कुछ डिलीट), तो आप निम्नलिखित कर सकते हैं ( बेहतर प्रदर्शन देने के (select max(rowid) from foo)+1बजाय max(rowid)+1, जैसा कि टिप्पणियों में बताया गया है):

select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1));

यदि आपके पास छेद हैं, तो आप कभी-कभी एक गैर-विद्यमान उपद्रवी का चयन करने का प्रयास करेंगे, और चयन एक खाली परिणाम सेट लौटाएगा। यदि यह स्वीकार्य नहीं है, तो आप इस तरह एक डिफ़ॉल्ट मान प्रदान कर सकते हैं:

select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1)) or rowid = (select max(rowid) from node) order by rowid limit 1;

यह दूसरा समाधान सही नहीं है: संभाव्यता का वितरण अंतिम पंक्ति (उच्चतम पंक्ति के साथ एक) पर अधिक है, लेकिन यदि आप अक्सर तालिका में सामान जोड़ते हैं, तो यह एक चलती लक्ष्य बन जाएगा और संभावनाओं का वितरण होना चाहिए काफी बेहतर।

फिर भी एक और समाधान, यदि आप अक्सर बहुत सारे छेद वाली मेज से यादृच्छिक सामान का चयन करते हैं, तो आप एक ऐसी तालिका बनाना चाहते हैं जिसमें मूल तालिका की पंक्तियों को यादृच्छिक क्रम में क्रमबद्ध किया गया हो:

create table random_foo(foo_id);

फिर, आवधिक, तालिका random_foo फिर से भरें

delete from random_foo;
insert into random_foo select id from foo;

और एक यादृच्छिक पंक्ति का चयन करने के लिए, आप मेरी पहली विधि का उपयोग कर सकते हैं (यहां कोई छेद नहीं हैं)। बेशक, इस अंतिम विधि में कुछ समसामयिक समस्याएं हैं, लेकिन random_foo का पुन: निर्माण एक अनुरक्षण ऑपरेशन है जो बहुत बार होने की संभावना नहीं है।

फिर भी, अभी तक एक और तरीका है, जो मुझे हाल ही में एक मेलिंग सूची में मिला है , सबसे बड़ी उपद्रवी के साथ पंक्ति को वर्तमान हटाए गए पंक्ति में स्थानांतरित करने के लिए एक ट्रिगर डालना है, ताकि कोई छेद न बचा हो।

अंतिम रूप से, ध्यान दें कि उपद्रवी और पूर्णांक प्राथमिक कुंजी अभिवाही का व्यवहार समरूप नहीं है (पंक्ति के साथ, जब एक नई पंक्ति सम्मिलित की जाती है, तो अधिकतम (पंक्तिबद्ध) +1 चुना जाता है, जबकि यह हाइगेस्ट-मूल्य-कभी-देखा + 1 है एक प्राथमिक कुंजी), इसलिए अंतिम समाधान random_foo में एक संकेत के साथ काम नहीं करेगा, लेकिन अन्य तरीके होंगे।


जैसा मैं सिर्फ एक मेलिंग सूची पर देखा, बजाय वापस आने विधि (विधि 2) होने का, तो आप सिर्फ उपयोग कर सकते हैं rowid> = [यादृच्छिक] के बजाय = की, लेकिन यह वास्तव slugissingly धीमी विधि 2. की तुलना में है
सुजान Dupéron

3
यह एक महान जवाब है; हालाँकि इसमें एक समस्या है। SELECT max(rowid) + 1धीमी क्वेरी होगी - इसके लिए एक पूर्ण तालिका स्कैन की आवश्यकता होती है। sqlite केवल क्वेरी का अनुकूलन करता है SELECT max(rowid)। इस प्रकार, यह उत्तर इससे बेहतर होगा: select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1)); इसे अधिक जानकारी के लिए देखें: sqlite.1065341.n5.nabble.com/…
dasl

19

आपको अपनी क्वेरी पर "ऑर्डर द्वारा रैंडम ()" डालने की आवश्यकता है ।

उदाहरण:

select * from quest order by RANDOM();

आइए एक पूर्ण उदाहरण देखें

  1. एक तालिका बनाएँ:
CREATE TABLE  quest  (
    id  INTEGER PRIMARY KEY AUTOINCREMENT,
    quest TEXT NOT NULL,
    resp_id INTEGER NOT NULL
);

कुछ मूल्यों को सम्मिलित करना:

insert into quest(quest, resp_id) values ('1024/4',6), ('256/2',12), ('128/1',24);

एक डिफ़ॉल्ट चयन:

select * from quest;

| id |   quest  | resp_id |
   1     1024/4       6
   2     256/2       12
   3     128/1       24
--

एक यादृच्छिक का चयन करें:

select * from quest order by RANDOM();
| id |   quest  | resp_id |
   3     128/1       24
   1     1024/4       6
   2     256/2       12
--
* हर बार जब आप चुनते हैं, तो ऑर्डर अलग होगा।

यदि आप केवल एक पंक्ति वापस करना चाहते हैं

select * from quest order by RANDOM() LIMIT 1;
| id |   quest  | resp_id |
   2     256/2       12
--
* हर बार जब आप चयन करते हैं, तो रिटर्न अलग होगा।


हालांकि कोड-ओनली उत्तरों को निषिद्ध नहीं किया गया है, कृपया यह समझें कि यह एक प्रश्नोत्तर समुदाय है, बजाय एक भीड़-सोर्सिंग के, और वह, आमतौर पर, यदि ओपी एक कोड के रूप में पोस्ट किए जा रहे उत्तर को समझ जाता है, तो वह / वह ऊपर आ जाएगा। अपने स्वयं के समान समाधान के साथ, और पहली जगह में एक प्रश्न पोस्ट नहीं किया होगा। जैसे, प्रदान करें संदर्भ के बारे में समझाकर अपने जवाब और / या कोड को कैसे और / या क्यों यह काम करता है।
XenoRo

2
मैं इस समाधान को पसंद करता हूं, क्योंकि यह मुझे एन लाइनों की खोज करने की अनुमति देता है। मेरे मामले में, मुझे डेटाबेस से 100 यादृच्छिक नमूनों की आवश्यकता थी - ORDER BY RANDOM () के साथ संयुक्त LIMIT 100 ठीक यही करता है।
mnr

17

व्हाट अबाउट:

SELECT COUNT(*) AS n FROM foo;

फिर [0, n) और में एक यादृच्छिक संख्या m चुनें

SELECT * FROM foo LIMIT 1 OFFSET m;

आप पहले नंबर ( n ) को भी कहीं सेव कर सकते हैं और डेटाबेस में बदलाव होने पर ही इसे अपडेट कर सकते हैं। इस तरह आपको हर बार SELECT COUNT नहीं करना है।


1
यह एक अच्छा तेज़ तरीका है। यह 1 से अधिक पंक्ति का चयन करने के लिए बहुत अच्छी तरह से सामान्यीकरण नहीं करता है, लेकिन ओपी ने केवल 1 के लिए कहा है, इसलिए मुझे लगता है कि यह ठीक है।
केन विलियम्स

ध्यान देने वाली बात यह है कि OFFSETऑफसेट के आकार के आधार पर लगता है कि खोजने के लिए आवश्यक समय है - पंक्ति 2 तेज है, पंक्ति 2 मिलियन थोड़ी देर लगती है, तब भी जब डेटा का सभी डेटा निश्चित आकार का होता है और यह इसे सीधे लेने में सक्षम होना चाहिए। कम से कम, यह वही है जो SQLite 3.7.13 में दिखता है।
केन विलियम्स

@KenWilliams बहुत अधिक सभी डेटाबेस `OFFSET`` के साथ एक ही समस्या है। एक डेटाबेस को क्वेरी करने के लिए यह एक बहुत ही अक्षम तरीका है क्योंकि इसे पढ़ने की आवश्यकता है कि कई पंक्तियां भले ही यह केवल 1 लौटेंगी।
जोनाथन एलन

1
ध्यान दें कि मैं / तय आकार / रिकॉर्ड के बारे में बात कर रहा था, हालांकि - डेटा में सीधे बाइट को स्कैन करना आसान होना चाहिए ( कई पंक्तियों को पढ़ना नहीं ), लेकिन उन्हें अनुकूलन को स्पष्ट रूप से लागू करना होगा।
केन विलियम्स

@ केनविलियम्स: SQLite में निश्चित आकार के रिकॉर्ड नहीं हैं, यह गतिशील रूप से टाइप किया गया है और डेटा को घोषित संपन्नताओं ( sqlite.org/fileformat2.html#section_2_1 ) से मेल नहीं खाता है । सब कुछ बी-ट्री पृष्ठों में संग्रहीत किया जाता है, इसलिए या तो इसे पत्ती की ओर कम से कम बी-ट्री खोज करना है। इसे कुशलतापूर्वक पूरा करने के लिए प्रत्येक बच्चे के सूचक के साथ-साथ उप-आकार का भंडारण करना होगा। यह बहुत कम लाभ के लिए एक ओवरहेड होगा, जैसा कि आप अभी भी शामिल होने के लिए OFFSET का अनुकूलन करने में सक्षम नहीं होंगे, आदि द्वारा आदेश ... (और आदेश के बिना आदेश अपरिभाषित है।)
याकोव गॉल

13
SELECT   bar
FROM     foo
ORDER BY Random()
LIMIT    1

11
चूँकि यह पहले पूरी तालिका सामग्री का चयन करेगा, इसलिए क्या यह बड़ी तालिकाओं के लिए बहुत समय लेने वाला नहीं होगा?
एलेक्स_कोडर

1
क्या आप केवल "WHERE" स्थिति (स्थितियों) का उपयोग कर दायरा सीमित नहीं कर सकते?
जुलडूपोंट

11

यहाँ @ ak के समाधान का एक संशोधन है:

SELECT * 
FROM table
LIMIT 1 
OFFSET ABS(RANDOM()) % MAX((SELECT COUNT(*) FROM table), 1)

यह समाधान अंतराल के साथ सूचकांकों के लिए भी काम करता है, क्योंकि हम एक सीमा में ऑफसेट को यादृच्छिक करते हैं [0, गिनती)। MAXखाली तालिका के साथ एक मामले को संभालने के लिए उपयोग किया जाता है।

यहां 16k पंक्तियों के साथ एक तालिका पर सरल परीक्षा परिणाम दिए गए हैं:

sqlite> .timer on
sqlite> select count(*) from payment;
16049
Run Time: real 0.000 user 0.000140 sys 0.000117

sqlite> select payment_id from payment limit 1 offset abs(random()) % (select count(*) from payment);
14746
Run Time: real 0.002 user 0.000899 sys 0.000132
sqlite> select payment_id from payment limit 1 offset abs(random()) % (select count(*) from payment);
12486
Run Time: real 0.001 user 0.000952 sys 0.000103

sqlite> select payment_id from payment order by random() limit 1;
3134
Run Time: real 0.015 user 0.014022 sys 0.000309
sqlite> select payment_id from payment order by random() limit 1;
9407
Run Time: real 0.018 user 0.013757 sys 0.000208

4

मैं बड़े sqlite3 डेटाबेस के लिए निम्नलिखित समाधान के साथ आया :

SELECT * FROM foo WHERE rowid = abs(random()) % (SELECT max(rowid) FROM foo) + 1; 

एब्स (X) फ़ंक्शन, संख्यात्मक तर्क X का पूर्ण मान लौटाता है।

यादृच्छिक () फ़ंक्शन -9223372036854775808 और +9223372036854775807 के बीच एक छद्म यादृच्छिक पूर्णांक देता है।

ऑपरेटर% अपने बाएं ऑपरेंड modulo के दाहिने ऑपरेंड के पूर्णांक मान को आउटपुट करता है।

अंत में, आप पंक्ति को 0 के बराबर पंक्ति को रोकने के लिए जोड़ते हैं।


1
अच्छी कोशिश, लेकिन मुझे नहीं लगता कि यह काम करेगा। क्या होगा यदि rowId = 5 के साथ एक पंक्ति को हटा दिया गया था, लेकिन rowIds 1,2,3,4,6,7,8,9,9,10 अभी भी मौजूद हैं? फिर, यदि चुनी गई यादृच्छिक पंक्ति 5 है, तो यह क्वेरी कुछ भी नहीं लौटाएगी।
कैलिकोडर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.