ActiveRecord में रैंडम रिकॉर्ड


151

मैं ActiveRecord के माध्यम से एक तालिका से एक यादृच्छिक रिकॉर्ड प्राप्त करने की आवश्यकता है। मैंने 2006 से जैमिस बक से उदाहरण लिया है ।

हालाँकि, मैं Google खोज के माध्यम से दूसरे तरीके से भी आया हूं (नए उपयोगकर्ता प्रतिबंधों के कारण लिंक के साथ विशेषता नहीं दे सकता)

 rand_id = rand(Model.count)
 rand_record = Model.first(:conditions => ["id >= ?", rand_id])

मैं उत्सुक हूं कि यहां दूसरों ने इसे कैसे किया है या अगर किसी को पता है कि किस तरह से अधिक कुशल होगा।


2
2 अंक जो एक उत्तर की मदद कर सकते हैं। 1. कैसे समान रूप से वितरित आपकी आईडी हैं, क्या वे अनुक्रमिक हैं? 2. कितना यादृच्छिक होना चाहिए? अच्छा पर्याप्त यादृच्छिक, या वास्तविक यादृच्छिक?
माइकल

वे अनुक्रमिक आईडी हैं जो एक्टिवरकॉर्ड द्वारा उत्पन्न ऑटो हैं और इसे बस काफी अच्छा होना है।
jyunderwood

1
तब आपका प्रस्तावित समाधान आदर्श के करीब है :) मैं COUNT (*) के बजाय "SELECT MAX (id) FROM table_name" का उपयोग करूंगा क्योंकि यह हटाई गई पंक्तियों के साथ थोड़ा बेहतर व्यवहार करेगा, अन्यथा, बाकी ठीक है। संक्षेप में, यदि "अच्छा पर्याप्त" ठीक है, तो आपके पास बस एक तरीका होना चाहिए जो आपके पास वास्तव में जो कुछ है उसके करीब वितरण को मानता है। यदि यह समान है और यहां तक ​​कि जैसा कि आपने कहा है, साधारण रैंड महान काम करता है।
माइकल

1
जब आप पंक्तियों को हटा चुके हों तो यह काम नहीं करेगा।
वेंकट डी।

जवाबों:


136

मुझे कम से कम दो प्रश्नों के बिना ऐसा करने का एक आदर्श तरीका नहीं मिला है।

एक ऑफसेट के रूप में निम्नलिखित बेतरतीब ढंग से उत्पन्न संख्या (वर्तमान रिकॉर्ड संख्या तक) का उपयोग करता है ।

offset = rand(Model.count)

# Rails 4
rand_record = Model.offset(offset).first

# Rails 3
rand_record = Model.first(:offset => offset)

ईमानदार होने के लिए, मैं सिर्फ ORDER BY RAND () या RANDOM () (डेटाबेस के आधार पर) का उपयोग कर रहा हूं। यदि आपके पास प्रदर्शन समस्या नहीं है, तो यह प्रदर्शन समस्या नहीं है।


2
कोड Model.find(:offset => offset).firstत्रुटि फेंक देगा। मुझे लगता है कि Model.first(:offset => offset)बेहतर प्रदर्शन कर सकते हैं।
हरीश शेट्टी

1
हाँ, मैं रेल 3 के साथ काम कर रहा हूं और संस्करणों के बीच क्वेरी प्रारूपों के बारे में भ्रमित हो रहा हूं।
टोबी हैड

7
ध्यान दें कि बड़े डेटासेट के साथ ऑफसेट का उपयोग करना बहुत धीमा है, क्योंकि इसे वास्तव में इंडेक्स स्कैन (या टेबल स्कैन की आवश्यकता होती है, अगर क्लस्टर इंडेक्स का उपयोग InnoDB की तरह किया जाता है)। दूसरे शब्दों में, यह O (N) ऑपरेशन है, लेकिन "WHERE id> = # {rand_id} ORDER BY id ASC LIMIT 1" O (लॉग एन) है, जो बहुत तेज है।
Kenn

15
ज्ञात हो कि ऑफ़सेट-अप्रोच केवल एक यादृच्छिक रूप से पाए जाने वाले डेटा पॉइंट (पहले, सभी के बाद भी आईडी द्वारा सॉर्ट किए जाते हैं) को प्राप्त करता है। यदि आपको कई बेतरतीब ढंग से चयनित रिकॉर्ड की आवश्यकता है, तो आपको कई बार इस दृष्टिकोण का उपयोग करना होगा या अपने डेटाबेस द्वारा प्रदान की गई यादृच्छिक क्रम विधि का उपयोग करना होगा, अर्थात Thing.order("RANDOM()").limit(100)100 यादृच्छिक रूप से चयनित प्रविष्टियों के लिए। (ध्यान रखें कि यह RANDOM()PostgreSQL और RAND()MySQL में है ... उतना पोर्टेबल नहीं है जितना आप इसे चाहते हैं।)
फ्लोरियन पिल्ज़

3
मेरे लिए पटरियों 4 पर काम नहीं करता है। उपयोग करें Model.offset(offset).first
Mahemoff

206

रेलें ६

जैसा कि टिप्पणी में जेसन द्वारा कहा गया है, रेल 6 में, गैर-विशेषता तर्कों की अनुमति नहीं है। आपको एक Arel.sql()बयान में मूल्य को लपेटना होगा ।

Model.order(Arel.sql('RANDOM()')).first

रेल 5, 4

में रेल 4 और 5 , का उपयोग कर Postgresql या SQLite , का उपयोग करते हुए RANDOM():

Model.order('RANDOM()').first

मुमकिन है उसी के लिए काम करेगा MySQL के साथRAND()

Model.order('RAND()').first

यह स्वीकृत उत्तर में दृष्टिकोण की तुलना में लगभग 2.5 गुना तेज है ।

कैविएट : यह लाखों रिकॉर्ड वाले बड़े डेटासेट के लिए धीमा है, इसलिए आप एक limitक्लॉज़ जोड़ना चाह सकते हैं ।


4
"रैंडम ()" भी sqlite में काम करता है, इसलिए हममें से जो अभी भी sqlite पर विकास कर रहे हैं और उत्पादन में पोस्टग्रेज कर रहे हैं, आपका समाधान दोनों वातावरणों में काम करता है।
wuliwong

5
मैंने स्वीकृत उत्तर के खिलाफ इसके लिए एक बेंचमार्क बनाया । Postgresql 9.4 पर इस उत्तर का दृष्टिकोण लगभग दोगुना तेज़ है।
पनमारी

3
ऐसा लगता है कि यह mysql webtrenches.com/post.cfm/avoid-rand-in-mysql
प्रकाश मूर्ति

यह सबसे तेज़ समाधान है
सर्जियो बेलेव्स्किज

1
"गैर-विशेषता तर्क को रेल 6.0 में अस्वीकृत कर दिया जाएगा। इस पद्धति को उपयोगकर्ता-प्रदान किए गए मानों के साथ नहीं बुलाया जाना चाहिए, जैसे अनुरोध पैरामीटर या मॉडल विशेषताएँ। ज्ञात-सुरक्षित मान उन्हें Arel.sql () में लपेटकर पारित किया जा सकता है।"
ट्रेंटन टायलर

73

आपका उदाहरण कोड गलत तरीके से व्यवहार करना शुरू कर देगा एक बार रिकॉर्ड हटा दिए जाते हैं (यह निचले आईडी के साथ गलत तरीके से आइटमों का पक्ष लेंगे)

आप शायद अपने डेटाबेस के भीतर यादृच्छिक तरीकों का उपयोग कर रहे हैं। ये भिन्न हो सकते हैं जिसके आधार पर DB आप उपयोग कर रहे हैं, लेकिन: ऑर्डर => "RAND ()" mysql के लिए काम करता है और: ऑर्डर => "RANDOM ()" पोस्टग्रेज के लिए काम करता है

Model.first(:order => "RANDOM()") # postgres example

7
डेटा बढ़ने पर MySQL के लिए ORDER BY RAND () भयावह क्रम में समाप्त हो जाता है। यह अचूक है (समय की आवश्यकताओं के आधार पर) यहां तक ​​कि सिर्फ हजारों पंक्तियों से शुरू होता है।
माइकल

माइकल एक महान बिंदु लाता है (जो अन्य डीबी के लिए भी सच है)। आम तौर पर बड़ी तालिकाओं से यादृच्छिक पंक्तियों का चयन करना कुछ ऐसा नहीं है जिसे आप एक गतिशील क्रिया में करना चाहते हैं। कैशिंग आपका दोस्त है। रिथिंकिंग जिसे आप पूरा करने की कोशिश कर रहे हैं वह एक बुरा विचार भी नहीं हो सकता है।
अर्धवार्षिक

1
एक लाख पंक्तियों के साथ एक मेज पर mysql में RAND () का आदेश देना slooooooooooooooooooooow है।
सबमेज

24
अब काम नहीं करता है। Model.order("RANDOM()").firstइसके बजाय उपयोग करें ।
फिल पिरोजोवकोव

धीमा और डेटाबेस विशिष्ट। ActiveRecord डेटाबेस के बीच मूल रूप से काम करने वाला है, इसलिए आपको इस विधि का उपयोग नहीं करना चाहिए।
डेक्स

29

MySQL 5.1.49, रूबी 1.9.2p180 पर इन दो तरीकों को बेंचमार्किंग के साथ + 5 मिलियन डॉलर उत्पाद की तालिका पर देखें:

def random1
  rand_id = rand(Product.count)
  rand_record = Product.first(:conditions => [ "id >= ?", rand_id])
end

def random2
  if (c = Product.count) != 0
    Product.find(:first, :offset =>rand(c))
  end
end

n = 10
Benchmark.bm(7) do |x|
  x.report("next id:") { n.times {|i| random1 } }
  x.report("offset:")  { n.times {|i| random2 } }
end


             user     system      total        real
next id:  0.040000   0.000000   0.040000 (  0.225149)
offset :  0.020000   0.000000   0.020000 ( 35.234383)

MySQL में ऑफसेट बहुत धीमा प्रतीत होता है।

EDIT I भी आजमाया

Product.first(:order => "RAND()")

लेकिन मुझे इसे ~ 60 सेकंड के बाद मारना था। MySQL "डिस्क पर tmp तालिका के लिए नकल" था। वह काम करने वाला नहीं है।


1
अधिक परीक्षणों की तलाश करने वालों के लिए वास्तविक यादृच्छिक दृष्टिकोण में कितना समय लगता है: मैंने Thing.order("RANDOM()").first250k प्रविष्टियों के साथ एक मेज पर कोशिश की - क्वेरी आधे सेकंड के नीचे समाप्त हो गई। (PostgreSQL 9.0, REE 1.8.7, 2 x 2.66 GHz कोर) यह मेरे लिए काफी तेज़ है, क्योंकि मैं एक बार "क्लीनअप" कर रहा हूं।
फ्लोरियन पिल्ज़

6
रूबी का रैंड मेथड एक कम लौटाता है जो आपको निर्दिष्ट संख्या चाहिए rand_id = rand(Product.count) + 1या फिर आपको अंतिम रिकॉर्ड कभी नहीं मिलेगा।
रिची

4
random1यदि आप कभी तालिका में कोई पंक्ति हटाते हैं तो नोट काम नहीं करेगा। (गणना अधिकतम आईडी से कम होगी और आप कभी भी उच्च आईडी वाली पंक्तियों का चयन नहीं कर पाएंगे)।
निकोलस

का उपयोग करते हुए random2एक से सुधार किया जा सकता #orderएक अनुक्रमित स्तंभ का उपयोग।
कार्सन रिंकी

18

यह इतना मुश्किल नहीं है।

ids = Model.pluck(:id)
random_model = Model.find(ids.sample)

pluckतालिका में सभी आईडी की एक सरणी देता है। sampleसरणी पर विधि, सरणी से एक यादृच्छिक आईडी देता है।

हटाए गए पंक्तियों के साथ तालिकाओं के चयन और समर्थन की समान संभावना के साथ, यह अच्छा प्रदर्शन करना चाहिए। आप इसे बाधाओं के साथ भी मिला सकते हैं।

User.where(favorite_day: "Friday").pluck(:id)

और इस तरह एक यादृच्छिक उपयोगकर्ता चुनें जो केवल किसी उपयोगकर्ता के बजाय शुक्रवार को पसंद करता है।


8
यह साफ है और एक छोटी मेज या एक बार उपयोग के लिए काम करता है, बस ध्यान दें कि यह पैमाने पर नहीं होगा। 3M टेबल पर, IDDB मारियाडीबी पर मेरे लिए लगभग 15 सेकंड लेती है।
Mahemoff

2
ये एक अच्छा बिंदु है। क्या आपने समान गुणों को बनाए रखते हुए एक वैकल्पिक समाधान पाया है जो तेज है?
नील्स बी।

क्या स्वीकृत ऑफसेट समाधान समान गुणों को बनाए नहीं रखता है?
Mahemoff

नहीं, यह शर्तों का समर्थन नहीं करता है और हटाए गए रिकॉर्ड के साथ तालिकाओं के चयन की समान संभावना नहीं है।
नील्स बी।

1
यह सोचने के लिए आओ, यदि आप बाधाओं को लागू करते हैं जब दोनों गिनती और एक ऑफसेट के साथ चयन करते हैं, तो तकनीक को काम करना चाहिए। मैं इसे केवल गिनती पर लागू करने की कल्पना कर रहा था।
नील्स बी।

15

यह सलाह नहीं दी जाती है कि आप इस समाधान का उपयोग करें, लेकिन यदि किसी कारण से आप वास्तव में केवल एक डेटाबेस क्वेरी बनाते समय रिकॉर्ड को बेतरतीब ढंग से चुनना चाहते हैं, तो आप रूबी ऐरे वर्गsample से विधि का उपयोग कर सकते हैं , जो आपको एक यादृच्छिक आइटम का चयन करने की अनुमति देता है। एक सरणी से।

Model.all.sample

इस पद्धति के लिए केवल डेटाबेस क्वेरी की आवश्यकता होती है, लेकिन यह वैकल्पिक विकल्पों की तुलना में काफी धीमी है, Model.offset(rand(Model.count)).firstजिसके लिए दो डेटाबेस प्रश्नों की आवश्यकता होती है, हालांकि बाद वाले को अभी भी पसंद किया जाता है।


99
यह मत करो। कभी।
ज़ब्बा

5
यदि आपके डेटाबेस में 100k पंक्तियाँ हैं, तो इन सभी को मेमोरी में लोड करना होगा।
वेंकट डी।

3
बेशक यह उत्पादन वास्तविक समय कोड के लिए अनुशंसित नहीं है, लेकिन मैं इस समाधान की तरह, यह की तरह विशेष परिस्थितियों के लिए उपयोग करने के लिए बहुत स्पष्ट है बोने नकली मूल्यों के साथ डेटाबेस।
fguillen

13
कृपया - कभी मत कहो। यह विकास-समय डिबगिंग के लिए एक महान समाधान है यदि तालिका छोटी है। (और यदि आप नमूने ले रहे हैं, तो डिबगिंग संभवतः उपयोग का मामला है)।
Mahemoff

Im बोने के लिए उपयोग कर रहा हूं और मेरे लिए अच्छा है। इसके अलावा, Model.all.sample (n) भी काम करता है :)
अर्नाल्डो इग्नासियो गस्पार वेजर

13

मैंने इसे संभालने के लिए एक रेल 3 मणि बनाई:

https://github.com/spilliton/randumb

यह आपको इस तरह सामान बनाने की अनुमति देता है:

Model.where(:column => "value").random(10)

7
इस मणि के प्रलेखन में वे समझाते हैं "रंडंब बस आपकी क्वेरी के लिए एक अतिरिक्त ORDER BY RANDOM()(या RAND()mysql के लिए) काटता है ।" - इसलिए, @semanticart द्वारा उत्तर के लिए टिप्पणियों में उल्लिखित बुरे प्रदर्शन पर टिप्पणी भी इस मणि का उपयोग करते समय लागू होती है। लेकिन कम से कम यह डीबी स्वतंत्र है।
निकोलस

8

मैं इसे अक्सर उपयोग करता हूं कंसोल से मैं एक आरंभिक में ActiveRecord का विस्तार करता हूं - रेल 4 उदाहरण:

class ActiveRecord::Base
  def self.random
    self.limit(1).offset(rand(self.count)).first
  end
end

मैं फिर Foo.randomएक यादृच्छिक रिकॉर्ड वापस लाने के लिए कॉल कर सकता हूं ।


1
क्या आपको चाहिए limit(1)? ActiveRecord#firstऐसा करने के लिए पर्याप्त स्मार्ट होना चाहिए।
टोकन

6

Postgres में एक प्रश्न:

User.order('RANDOM()').limit(3).to_sql # Postgres example
=> "SELECT "users".* FROM "users" ORDER BY RANDOM() LIMIT 3"

एक ऑफसेट का उपयोग करते हुए, दो प्रश्न:

offset = rand(User.count) # returns an integer between 0 and (User.count - 1)
Model.offset(offset).limit(1)

1
-1 की जरूरत नहीं है, रैंड की गिनती संख्या 1 तक है
एनीमिया

धन्यवाद, परिवर्तित: +1:
थॉमस क्लेम

5

इन सभी को पढ़ने से मुझे इस बात का बहुत अधिक विश्वास नहीं हुआ कि इनमें से कौन सी मेरी विशेष स्थिति में रेल्स 5 और MySQL / मारिया 5.5 के साथ सबसे अच्छा काम करेगा। इसलिए मैंने ~ 65000 रिकॉर्ड्स में से कुछ जवाबों का परीक्षण किया, और उनमें से दो लेने के रास्ते हैं:

  1. रैंड () limitएक स्पष्ट विजेता है।
  2. pluck+ का उपयोग न करें sample
def random1
  Model.find(rand((Model.last.id + 1)))
end

def random2
  Model.order("RAND()").limit(1)
end

def random3
  Model.pluck(:id).sample
end

n = 100
Benchmark.bm(7) do |x|
  x.report("find:")    { n.times {|i| random1 } }
  x.report("order:")   { n.times {|i| random2 } }
  x.report("pluck:")   { n.times {|i| random3 } }
end

              user     system      total        real
find:     0.090000   0.000000   0.090000 (  0.127585)
order:    0.000000   0.000000   0.000000 (  0.002095)
pluck:    6.150000   0.000000   6.150000 (  8.292074)

यह उत्तर मोहम्मद के उत्तर को संश्लेषित, सत्यापित और अद्यतन करता है, साथ ही नमामी वांग की टिप्पणी पर भी और फ्लोरियन पिल्ज़ की स्वीकृत उत्तर पर टिप्पणी - कृपया उन्हें वोट भेजें!


3

आप उपयोग कर सकते हैं Arrayविधि sampleविधि, sample, एक सरणी से एक यादृच्छिक ऑब्जेक्ट है ताकि इसे आप सिर्फ एक सरल में कार्यकारी करने की आवश्यकता का उपयोग करने के ActiveRecordक्वेरी है कि एक संग्रह, उदाहरण के लिए:

User.all.sample

कुछ इस तरह लौटेगा:

#<User id: 25, name: "John Doe", email: "admin@example.info", created_at: "2018-04-16 19:31:12", updated_at: "2018-04-16 19:31:12">

मैं एआर का उपयोग करते समय सरणी विधियों के साथ काम करने की सिफारिश नहीं करूंगा। इस तरह से लगभग 8 गुना समय order('rand()').limit(1)लगता है "वही" काम (~ 10K रिकॉर्ड के साथ)।
सेबेस्टियन पाल्मा

3

रैंडम रिकॉर्ड्स के लिए इस रत्न की जोरदार अनुशंसा करें, जो विशेष रूप से बहुत सारी डेटा पंक्तियों वाली तालिका के लिए डिज़ाइन किया गया है:

https://github.com/haopingfan/quick_random_records

इस मणि को छोड़कर अन्य सभी उत्तर बड़े डेटाबेस के साथ खराब प्रदर्शन करते हैं:

  1. quick_random_records केवल 4.6msपूरी तरह से लागत ।

यहाँ छवि विवरण दर्ज करें

  1. User.order('RAND()').limit(10)लागत 733.0ms

यहाँ छवि विवरण दर्ज करें

  1. स्वीकृत उत्तर offsetदृष्टिकोण लागत 245.4msपूरी तरह से।

यहाँ छवि विवरण दर्ज करें

  1. User.all.sample(10)दृष्टिकोण लागत 573.4ms

यहाँ छवि विवरण दर्ज करें


नोट: मेरी तालिका में केवल 120,000 उपयोगकर्ता हैं। आपके पास जितने अधिक रिकॉर्ड होंगे, प्रदर्शन का अंतर उतना ही अधिक होगा।


2

यदि आपको निर्दिष्ट दायरे में कुछ यादृच्छिक परिणाम चुनने की आवश्यकता है :

scope :male_names, -> { where(sex: 'm') }
number_of_results = 10

rand = Names.male_names.pluck(:id).sample(number_of_results)
Names.where(id: rand)

1

किसी सूची से आइटम को बेतरतीब ढंग से चुनने के लिए रूबी विधि है samplesampleActiveRecord के लिए एक कुशल बनाना चाहते हैं , और पिछले उत्तरों के आधार पर, मैंने उपयोग किया:

module ActiveRecord
  class Base
    def self.sample
      offset(rand(size)).first
    end
  end
end

मैंने इसे इसमें डाल दिया lib/ext/sample.rbऔर फिर इसे इसमें लोड किया config/initializers/monkey_patches.rb:

Dir[Rails.root.join('lib/ext/*.rb')].each { |file| require file }

यह एक क्वेरी होगी यदि मॉडल का आकार पहले से ही कैश है और दो अन्यथा।


1

रेल 4.2 और ओरेकल :

Oracle के लिए आप अपने मॉडल पर एक स्कोप सेट कर सकते हैं जैसे:

scope :random_order, -> {order('DBMS_RANDOM.RANDOM')}

या

scope :random_order, -> {order('DBMS_RANDOM.VALUE')}

और फिर एक नमूना के लिए इसे इस तरह से कॉल करें:

Model.random_order.take(10)

या

Model.random_order.limit(5)

निश्चित रूप से आप भी एक गुंजाइश के बिना एक आदेश जगह सकता है जैसे:

Model.all.order('DBMS_RANDOM.RANDOM') # or DBMS_RANDOM.VALUE respectively

आप इसे order('random()'और MySQL के साथ order('rand()')ही पोस्टग्रेज के साथ भी कर सकते हैं । यह निश्चित रूप से सबसे अच्छा जवाब है।
jrochkind

1

MySQL डेटाबेस के लिए प्रयास करें: Model.order ("RAND ()")। पहले


यह mysql पर काम नहीं करता है .. आपको कम से कम DB इंजन के साथ काम करने के लिए प्रेरित करना चाहिए
अर्नोल्ड रोआ

क्षमा करें, टाइपो था। अब तय हो गया। Mysql (केवल) के लिए काम करना चाहिए
वादिम Eremeev

1

यदि आप PostgreSQL 9.5+ का उपयोग कर रहे हैं, तो आप इसका लाभ उठा सकते हैं TABLESAMPLE यादृच्छिक रिकॉर्ड का चयन करने लिए ।

दो डिफ़ॉल्ट नमूना तरीकों ( SYSTEMऔर BERNOULLI) के लिए आवश्यक है कि आप तालिका में पंक्तियों की कुल संख्या के प्रतिशत के रूप में लौटने के लिए पंक्तियों की संख्या निर्दिष्ट करें।

-- Fetch 10% of the rows in the customers table.
SELECT * FROM customers TABLESAMPLE BERNOULLI(10);

इसके लिए उपयुक्त प्रतिशत का चयन करने के लिए तालिका में रिकॉर्ड की मात्रा जानना आवश्यक है, जो कि जल्दी से खोजना आसान नहीं होगा। सौभाग्य से, वहाँ tsm_system_rowsमॉड्यूल है जो आपको सीधे लौटने के लिए पंक्तियों की संख्या निर्दिष्ट करने की अनुमति देता है।

CREATE EXTENSION tsm_system_rows;

-- Fetch a single row from the customers table.
SELECT * FROM customers TABLESAMPLE SYSTEM_ROWS(1);

ActiveRecord के भीतर इसका उपयोग करने के लिए, पहले एक्सटेंशन को माइग्रेशन में सक्षम करें:

class EnableTsmSystemRowsExtension < ActiveRecord::Migration[5.0]
  def change
    enable_extension "tsm_system_rows"
  end
end

फिर fromक्वेरी के खंड को संशोधित करें :

customer = Customer.from("customers TABLESAMPLE SYSTEM_ROWS(1)").first

मुझे नहीं पता कि क्या SYSTEM_ROWSनमूना विधि पूरी तरह से यादृच्छिक होगी या यदि यह सिर्फ एक यादृच्छिक पृष्ठ से पहली पंक्ति लौटाती है।

यह जानकारी गुलसीन यिलदिरिम द्वारा लिखे गए 2ndQuadrant ब्लॉग पोस्ट से ली गई थी


1

इतने सारे उत्तरों को देखने के बाद मैंने उन सभी को अपने PostgreSQL (9.6.3) डेटाबेस पर बेंचमार्क करने का निर्णय लिया। मैं एक छोटे से 100,000 टेबल का उपयोग करता हूं और मॉडल.ऑर्डर ("रैंडम ()") से छुटकारा पा लिया है। पहले क्योंकि यह पहले से ही दो धीमी परिमाण के आदेश थे।

10 कॉलम के साथ 2,500,000 प्रविष्टियों के साथ एक तालिका का उपयोग करते हुए विजेता के हाथों की डाउन प्लक विधि लगभग 8 गुना तेज थी (ऑफसेट)। मैंने इसे केवल एक स्थानीय सर्वर पर चलाया था ताकि संख्या बढ़ जाए लेकिन इसका बड़ा हिस्सा प्लक हो सकता है। विधि वह है जिसका मैं उपयोग करूँगा। यह भी ध्यान देने योग्य बात है कि इससे समस्याएँ पैदा हो सकती हैं, क्योंकि आप एक समय में 1 से अधिक परिणाम प्राप्त कर सकते हैं क्योंकि उनमें से प्रत्येक अद्वितीय उर्फ ​​कम यादृच्छिक होगा।

प्लक मेरी 25,000,000 पंक्ति तालिका पर 100 बार दौड़ता है तथापि; यह रैम की एक उचित राशि लेता है।

RandomModel                 user     system      total        real
Model.find_by(id: i)       0.050000   0.010000   0.060000 (  0.059878)
Model.offset(rand(offset)) 0.030000   0.000000   0.030000 ( 55.282410)
Model.find(ids.sample)     6.450000   0.050000   6.500000 (  7.902458)

यहां यादृच्छिक रूप से शासन करने के लिए मेरी 100,000 पंक्ति तालिका पर 2000 बार डेटा चल रहा है

RandomModel       user     system      total        real
find_by:iterate  0.010000   0.000000   0.010000 (  0.006973)
offset           0.000000   0.000000   0.000000 (  0.132614)
"RANDOM()"       0.000000   0.000000   0.000000 ( 24.645371)
pluck            0.110000   0.020000   0.130000 (  0.175932)

1

बहुत पुराना प्रश्न लेकिन इसके साथ:

rand_record = Model.all.shuffle

आपको यादृच्छिक क्रम द्वारा रिकॉर्ड का एक एरियर मिला। रत्नों या लिपियों की जरूरत नहीं।

यदि आप एक रिकॉर्ड चाहते हैं:

rand_record = Model.all.shuffle.first

1
सबसे अच्छा विकल्प नहीं है, क्योंकि यह सभी रिकॉर्ड्स को मेमोरी में लोड करता है। इसके अलावा, shuffle.first==.sample
एंड्रयू रूजेनको

0

मैं RoR के लिए बिल्कुल नया हूँ लेकिन मुझे यह मेरे लिए काम करने के लिए मिला है:

 def random
    @cards = Card.all.sort_by { rand }
 end

इससे आया:

रूबी में एक सरणी को बेतरतीब ढंग से (हाथापाई) कैसे करें?


4
इसके बारे में बुरी बात यह है कि यह डेटाबेस से सभी कार्ड लोड करने जा रहा है। यह डेटाबेस के अंदर करने के लिए अधिक कुशल है।
एंटन कुज़मिन

तुम भी साथ फेरबदल कर सकते हैं array.shuffle। फिर भी, सावधान रहें, जैसा Card.allकि सभी कार्ड रिकॉर्ड्स को मेमोरी में लोड करेगा, जो अधिक अक्षम है और अधिक वस्तुओं के बारे में हम बात कर रहे हैं।
थॉमस क्लेम

0

क्या करना है:

rand_record = Model.find(Model.pluck(:id).sample)

मेरे लिए बहुत स्पष्ट है


0

मैं अपने ऐप पर सैम के उदाहरण की कोशिश करता हूं, बेंचमार्क के 4.2.8 का उपयोग करके अपने ऐप पर (मैंने यादृच्छिक के लिए 1.Category.count डाला, क्योंकि अगर यादृच्छिक 0 लेता है तो यह एक त्रुटि उत्पन्न करेगा (ActiveRecord :: RecordNotFound: नहीं मिलेगा 'आईडी' = 0) के साथ श्रेणी) और मेरा था:

 def random1
2.4.1 :071?>   Category.find(rand(1..Category.count))
2.4.1 :072?>   end
 => :random1
2.4.1 :073 > def random2
2.4.1 :074?>    Category.offset(rand(1..Category.count))
2.4.1 :075?>   end
 => :random2
2.4.1 :076 > def random3
2.4.1 :077?>   Category.offset(rand(1..Category.count)).limit(rand(1..3))
2.4.1 :078?>   end
 => :random3
2.4.1 :079 > def random4
2.4.1 :080?>    Category.pluck(rand(1..Category.count))
2.4.1 :081?>
2.4.1 :082 >     end
 => :random4
2.4.1 :083 > n = 100
 => 100
2.4.1 :084 > Benchmark.bm(7) do |x|
2.4.1 :085 >     x.report("find") { n.times {|i| random1 } }
2.4.1 :086?>   x.report("offset") { n.times {|i| random2 } }
2.4.1 :087?>   x.report("offset_limit") { n.times {|i| random3 } }
2.4.1 :088?>   x.report("pluck") { n.times {|i| random4 } }
2.4.1 :089?>   end

                  user      system      total     real
find            0.070000   0.010000   0.080000 (0.118553)
offset          0.040000   0.010000   0.050000 (0.059276)
offset_limit    0.050000   0.000000   0.050000 (0.060849)
pluck           0.070000   0.020000   0.090000 (0.099065)

0

.order('RANDOM()').limit(limit)साफ दिखता है, लेकिन बड़ी तालिकाओं के लिए धीमा है, क्योंकि इसे सभी पंक्तियों को प्राप्त करने और क्रमबद्ध करने की आवश्यकता है, भले ही limit1 (डेटाबेस में आंतरिक रूप से रेल में नहीं है)। मैं MySQL के बारे में निश्चित नहीं हूं लेकिन पोस्टग्रेज़ में ऐसा होता है। यहाँ और यहाँ में और अधिक स्पष्टीकरण ।

बड़ी मेज के लिए एक समाधान है, .from("products TABLESAMPLE SYSTEM(0.5)")जहां 0.5इसका मतलब है 0.5%। हालाँकि, मुझे लगता है कि यह समाधान अभी भी धीमा है यदि आपके पास WHEREऐसी स्थितियां हैं जो बहुत सारी पंक्तियों को फ़िल्टर करती हैं। मुझे लगता है कि यह है क्योंकि TABLESAMPLE SYSTEM(0.5)सभी पंक्तियों से पहले लाने के लिएWHERE शर्तों को लागू करने ।

बड़ी तालिकाओं के लिए एक और समाधान (लेकिन बहुत यादृच्छिक नहीं) है:

products_scope.limit(sample_size).sample(limit)

जहां sample_sizeहो सकता है 100(लेकिन बहुत बड़ा नहीं है अन्यथा यह धीमा है और बहुत अधिक मेमोरी का उपभोग करता है), और limitहो सकता है 1। ध्यान दें कि हालांकि यह तेज़ है, लेकिन यह वास्तव में यादृच्छिक नहीं है, यह sample_sizeकेवल रिकॉर्ड के भीतर यादृच्छिक है ।

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


0

उपयोग करने के साथ RANDOM(), आप इसे एक दायरे में भी फेंक सकते हैं:

class Thing
  scope :random, -> (limit = 1) {
    order('RANDOM()').
    limit(limit)
  }
end

या, यदि आप एक गुंजाइश के रूप में कल्पना नहीं करते हैं, तो बस एक वर्ग विधि में फेंक दें। अब Thing.randomसाथ काम करता है Thing.random(n)


0

"यादृच्छिक" के अर्थ पर निर्भर करता है और आप वास्तव में क्या करना चाहते हैं, take पर्याप्त हो सकता है।

यादृच्छिक के "अर्थ" से मेरा मतलब है:

  • क्या आपका मतलब है कि मुझे कोई भी तत्व दिया जाए जिसकी मुझे कोई परवाह नहीं है? तब यह काफी है।
  • अब, यदि आपका मतलब है "मुझे किसी भी तत्व को उचित संभावना के साथ दें जो दोहराया प्रयोगों से मुझे सेट से अलग तत्व मिलेंगे" तो, अन्य उत्तरों में वर्णित किसी भी विधि के साथ "लक" को बाध्य करें।

उदाहरण के लिए, परीक्षण के लिए, नमूना डेटा को अनियमित रूप से वैसे भी बनाया जा सकता था, इसलिए takeयह पर्याप्त से अधिक है, और ईमानदार होने के लिए भी first

https://guides.rubyonrails.org/active_record_querying.html#take

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.