ActiveRecord'da rastgele kayıt


151

ActiveRecord ile bir tablodan rastgele bir kayıt almam gerekiyor. 2006'dan Jamis Buck'ın örneğini takip ettim .

Ancak, bir Google aramasıyla başka bir yolla da karşılaştım (yeni kullanıcı kısıtlamaları nedeniyle bir bağlantıyla ilişkilendirilemez):

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

Buradaki başkalarının bunu nasıl yaptığını veya birisinin hangi yolun daha verimli olacağını bilmesi halinde merak ediyorum.


2
Bir cevap yardımcı olabilecek 2 puan. 1. Kimlikleriniz ne kadar eşit dağıtılıyor, sıralı mı? 2. Ne kadar rasgele olması gerekir? Yeterince iyi rastgele mi yoksa gerçek rastgele mi?
Michael

Bunlar activerecord tarafından otomatik olarak oluşturulan sıralı kimliklerdir ve sadece yeterince iyi olmalıdır.
jyunderwood

1
Sonra önerilen çözüm ideal yakın :) Ben COUNT (*) yerine "SELECT MAX (id) tablo_adı" kullanmak istiyorum, çünkü silinen satırları biraz daha iyi başa çıkacak, aksi takdirde, geri kalanı iyi. Kısacası, eğer "yeterince iyi" tamam, o zaman aslında sahip olduğunuza yakın bir dağıtım varsayalım bir yöntem olması gerekir. Düzgün ve söylediğiniz gibi bile, basit rand harika çalışıyor.
Michael

1
Satırları sildiğinizde bu çalışmaz.
Venkat D.

Yanıtlar:


136

En az iki sorgu olmadan bunu yapmak için ideal bir yol bulamadım.

Aşağıda, ofset olarak rastgele oluşturulmuş bir sayı (geçerli kayıt sayısına kadar) kullanılır .

offset = rand(Model.count)

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

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

Dürüst olmak gerekirse, ORDER BY RAND () veya RANDOM () kullanıyorum (veritabanına bağlı olarak). Bir performans sorununuz yoksa bu bir performans sorunu değildir.


2
Kod Model.find(:offset => offset).firsthata verir. Bence Model.first(:offset => offset)daha iyi performans gösterebilir.
Harish Shetty

1
Evet, ben Rails 3 ile çalışıyorum ve sürümleri arasındaki sorgu formatları hakkında karışmaya devam.
Toby Hede

7
Büyük veri kümesinde ofset kullanımının çok yavaş olduğuna dikkat edin, çünkü aslında dizin taraması (veya InnoDB gibi kümelenmiş dizin kullanılması durumunda tablo taraması) gerekir. Başka bir deyişle, O (N) işlemi ancak "WHERE id> = # {rand_id} ORDER BY id ASC LIMIT 1", O (log N).
kenn

15
Ofset yaklaşımının sadece rastgele bulunan tek bir veri noktası verdiğini unutmayın (ilki, hepsi sonradan hala kimliğe göre sıralanır). Birden fazla rasgele seçilmiş kayda ihtiyacınız varsa, bu yaklaşımı birden çok kez kullanmanız veya veritabanınız tarafından sağlanan rasgele sıra yöntemini kullanmanız gerekir; örneğin Thing.order("RANDOM()").limit(100), rasgele seçilen 100 giriş için. ( RANDOM()PostgreSQL ve RAND()MySQL'de olduğunu unutmayın ... olmasını istediğiniz kadar taşınabilir değil.)
Florian Pilz

3
Rails 4'te benim için çalışmıyor Model.offset(offset).first.
mahemoff

206

Raylar 6

Jason tarafından yorumlarda belirtildiği gibi, Rails 6'da nitelik olmayan argümanlara izin verilmez. Değeri bir Arel.sql()ifadeye sarmalısınız.

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

Raylar 5, 4

Olarak raylar 4 ve 5 kullanılarak, PostgreSQL'i veya SQLite'ı kullanılarak RANDOM():

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

Muhtemelen aynı şey MySQL içinRAND()

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

Bu , kabul edilen cevaptaki yaklaşımdan yaklaşık 2,5 kat daha hızlıdır .

Uyarı : Bu, milyonlarca kaydı olan büyük veri kümeleri için yavaştır, bu nedenle bir limitcümle eklemek isteyebilirsiniz .


4
"Random ()" da sqlite içinde çalışır, bu yüzden hala sqlite üzerinde gelişen ve üretimde postgres çalışanlarımız için, çözümünüz her iki ortamda da çalışır.
wuliwong

5
Kabul edilen cevaba karşı bunun için bir ölçüt oluşturdum . Postgresql 9.4'te bu cevabın yaklaşımı yaklaşık iki kat daha hızlıdır.
panmari


Bu en hızlı çözüm
Sergio Belevskij

1
"Rails 6.0'da öznitelik olmayan bağımsız değişkenlere izin verilmeyecektir. Bu yöntem, istek parametreleri veya model öznitelikleri gibi kullanıcı tarafından sağlanan değerlerle çağrılmamalıdır. Bilinen güvenli değerler bunları Arel.sql () öğesine sarılarak iletilebilir."
Trenton Tyler

73

Kayıtlar silindikten sonra örnek kodunuz yanlış davranmaya başlayacaktır (daha düşük kimliğe sahip öğeleri haksız şekilde tercih eder)

Veritabanınızdaki rasgele yöntemleri kullanmanız daha iyi olur. Bunlar, kullandığınız DB'ye bağlı olarak değişir, ancak: order => "RAND ()" mysql için ve: order => "RANDOM ()" postgres için çalışır

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

7
MySQL için ORDER BY RAND (), veri arttıkça korkunç çalışma süresinde sonuçlanır. Binlerce satırdan başlayarak bile (zaman gereksinimlerine bağlı olarak) sürdürülemez.
Michael

Michael büyük bir noktaya değiniyor (diğer DB'ler için de geçerli). Genellikle büyük tablolardan rastgele satırlar seçmek, dinamik bir eylemde yapmak istediğiniz bir şey değildir. Önbellek arkadaşın. Neyi başarmaya çalıştığınızı yeniden düşünmek de kötü bir fikir olmayabilir.
semanticart

1
Yaklaşık bir milyon satırlı bir tabloda mysql'de RAND () sipariş etmek slooooooooooooooooooooow.
Subimage

24
Artık çalışmıyor. Model.order("RANDOM()").firstBunun yerine kullanın .
phil pirozhkov

Yavaş ve veritabanına özel. ActiveRecord'un veritabanları arasında sorunsuz çalışması gerekiyor, bu nedenle bu yöntemi kullanmamalısınız.
Dex

29

Bu iki yöntemi MySQL 5.1.49, Ruby 1.9.2p180'de + 5 milyon kayıt içeren bir ürün tablosunda karşılaştırma:

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'deki ofset çok daha yavaş görünüyor.

DÜZENLE Ayrıca denedim

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

Ama ~ 60 saniye sonra öldürmek zorunda kaldım. MySQL "Diskteki tmp tablosuna kopyalama" idi. Bu işe yaramayacak.


1
Gerçek bir rastgele yaklaşımın ne kadar sürdüğünü daha fazla test arayanlar için: Thing.order("RANDOM()").first250k girişli bir masada denedim - sorgu yarım saniyenin altında tamamlandı. (PostgreSQL 9.0, REE 1.8.7, 2 x 2.66 GHz çekirdek) Bu benim için yeterince hızlı, çünkü bir kerelik "temizleme" yapıyorum.
Florian Pilz

6
Ruby'nin rand yöntemi, belirtilen sayıdan bir tane daha az döndürür, böylece isteyeceksiniz rand_id = rand(Product.count) + 1veya asla son kaydı almayacaksınız.
Ritchie

4
Not random1Hiç tablodaki bir satır silerseniz çalışmaz. (Sayım maksimum kimliğinden daha az olacaktır ve asla yüksek kimliğe sahip satırları seçemezsiniz).
Nicholas

Kullanarak random2bir iyileştirilebilir #orderdizinlenmiş bir kolon kullanılarak belirlendi.
Carson Reinke

18

O kadar zor olmak zorunda değil.

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

plucktablodaki tüm kimliklerin bir dizisini döndürür. sample yöntem diziden rastgele bir kimlik döndürür.

Bu, satırları silinen tablolar için eşit seçim ve destek olasılığı ile iyi performans göstermelidir. Kısıtlamalarla bile karıştırabilirsiniz.

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

Ve böylece herhangi bir kullanıcı yerine cumaları seven rastgele bir kullanıcı seçin.


8
Bu temiz ve küçük bir tablo veya bir kerelik kullanım için çalışır, sadece ölçeklenmeyeceğini unutmayın. 3M'lik bir tabloda, MariaDB'de kimliklerin koparılması benim için yaklaşık 15 saniye sürüyor.
mahemoff

2
İyi bir noktaya değindin. Aynı nitelikleri korurken daha hızlı alternatif bir çözüm buldunuz mu?
Niels B.

Kabul edilen ofset çözümü aynı niteliklere sahip değil mi?
mahemoff

Hayır, koşulları desteklemez ve silinmiş kayıtları olan tablolar için eşit bir seçim olasılığı yoktur.
Niels B.

1
Bunu düşünün, eğer bir sayım ile hem sayma hem de seçim yaparken kısıtlamaları uygularsanız, teknik çalışmalıdır. Sadece sayıma uyguladığımı hayal ediyordum.
Niels B.

15

Size bu çözümü kullanan bir nedenle eğer, ama bu tavsiye edilmez gerçekten sadece bir veritabanı sorgu yaparken rastgele bir kaydı seçmek istediğiniz şunu kullanabilirsiniz samplegelen yöntemini Yakut Array sınıfı rastgele bir öğe seçmenize olanak sağlar, bir diziden.

Model.all.sample

Bu yöntem yalnızca veritabanı sorgusu gerektirir, ancak yine de Model.offset(rand(Model.count)).firstiki veritabanı sorgusu gerektiren alternatiflerden önemli ölçüde yavaştır .


99
Bunu yapma. Hiç.
Zabba

5
Veritabanınızda 100 bin satır varsa, bunların tümünün belleğe yüklenmesi gerekir.
Venkat D.

3
Tabii ki üretim gerçek zamanlı kod için tavsiye edilmez, ancak bu çözümü beğendim , veritabanını sahte değerlerle tohumlama gibi özel durumlar için kullanmak çok açık .
fguillen

13
Lütfen - asla asla deme. Bu, tablo küçükse geliştirme zamanı hata ayıklaması için mükemmel bir çözümdür. (Örnek alıyorsanız, hata ayıklama büyük olasılıkla kullanım durumudur).
mahemoff

Ekim için kullanıyorum ve benim için iyi. Buna ek olarak, Model.all.sample (n) de çalışıyor :)
Arnaldo Ignacio Gaspar Véjar

13

Bunu idare etmek için bir raylar 3 mücevher yaptım:

https://github.com/spilliton/randumb

Bunun gibi şeyler yapmanızı sağlar:

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

7
Bu mücevher belgelerinde "randumb sadece bir ek ORDER BY RANDOM()(ya da RAND()mysql için) sorgunuza tacks ." - Bu nedenle, @semanticart'ın cevabına yapılan yorumlarda bahsedilen kötü performans hakkındaki yorumlar, bu gem kullanıldığında da geçerlidir. Ama en azından DB'den bağımsız.
Nicolas

8

Bunu konsoldan sık sık kullanıyorum, bir başlatıcıda ActiveRecord'u genişletiyorum - Rails 4 örneği:

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

Sonra Foo.randomrastgele bir kayıt geri getirmek için arayabilirim .


1
ihtiyacın var limit(1)ActiveRecord#firstbunu yapmak için yeterince akıllı olmalı.
tokland

6

Postgres'de bir sorgu:

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

Bir ofset kullanarak iki sorgu:

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

1
-1'e gerek yok, rand num - 1'e kadar sayar
anemaria20

Teşekkürler, değiştirildi: +1:
Thomas Klemm

5

Tüm bunları okumak, Rails 5 ve MySQL / Maria 5.5 ile özel durumumda hangisinin en iyi şekilde çalışacağı konusunda bana çok güven vermedi. Bu yüzden ~ 65000 kayıtlarındaki cevapların bazılarını test ettim ve iki yol aldım:

  1. A ile RAND () limitaçık bir kazanır.
  2. pluck+ Kullanmayın 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)

Bu cevap, Mohamed'in cevabının yanı sıra Nami WANG'ın aynı hakkındaki yorumunu ve Florian Pilz'un kabul edilen cevaba yaptığı yorumu sentezler, doğrular ve günceller - lütfen onlara oy verin!


3

Sen kullanabilirsiniz Arrayyöntemi sampleyöntemi samplebunu sadece basit içinde exec gerekir kullanmak için, bir diziden rastgele nesnesi döndüren ActiveRecordörneğin bir koleksiyon dönmek sorgusu:

User.all.sample

böyle bir şey döndürür:

#<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">

AR kullanırken dizi yöntemleri ile çalışma tavsiye etmem. Bu şekilde order('rand()').limit(1)"aynı" iş (~ 10K kayıtları ile) zaman neredeyse 8 kat alır .
Sebastian Palma

3

Çok sayıda veri satırı içeren tablo için özel olarak tasarlanmış rastgele kayıtlar için bu gem'i şiddetle tavsiye edin:

https://github.com/haopingfan/quick_random_records

Bu taş hariç diğer tüm yanıtlar büyük veritabanı ile kötü performans gösterir:

  1. quick_random_records'un maliyeti yalnızca 4.6mstoplam.

resim açıklamasını buraya girin

  1. User.order('RAND()').limit(10)maliyet 733.0ms.

resim açıklamasını buraya girin

  1. Kabul edilen cevap offsetyaklaşımı 245.4mstamamen maliyetlidir .

resim açıklamasını buraya girin

  1. User.all.sample(10)yaklaşım maliyeti 573.4ms.

resim açıklamasını buraya girin


Not: Masamın yalnızca 120.000 kullanıcısı var. Ne kadar çok kayda sahip olursanız, performans farkı o kadar büyük olur.


2

Belirtilen kapsamda rastgele bazı sonuçlar seçmeniz gerekiyorsa :

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

Listeden rastgele bir öğe seçmek için Ruby yöntemi kullanılır sample. sampleActiveRecord için verimli oluşturmak ve önceki yanıtlara dayanarak şunları kullandım:

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

Bu içine koymak lib/ext/sample.rbve daha sonra bu ile yükleyin config/initializers/monkey_patches.rb:

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

Bu, modelin boyutu önceden önbelleğe alınmışsa bir sorgu ve aksi takdirde iki sorgu olacaktır.


1

Raylar 4.2 ve Oracle :

Oracle için Modelinizde şu şekilde bir kapsam ayarlayabilirsiniz:

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

veya

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

Ve sonra bir örnek için şu şekilde çağırın:

Model.random_order.take(10)

veya

Model.random_order.limit(5)

tabii ki böyle bir kapsam olmadan da sipariş verebilirsiniz:

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

Bunu postgres ile order('random()'ve MySQL ile order('rand()')de yapabilirsiniz. Bu kesinlikle en iyi cevap.
jrochkind

1

MySQL veritabanı için şunu deneyin: Model.order ("RAND ()").


Bu mysql üzerinde çalışmıyor .. en azından hangi DB motoru ile çalışmak için varsayalım incloude gerekir
Arnold Roa

Üzgünüm, yazım hatası vardı. Şimdi düzeltildi. MySQL için çalışmalı (sadece)
Vadim Eremeev

1

PostgreSQL 9.5+ kullanıyorsanız, TABLESAMPLE rastgele bir kayıt seçmek .

İki varsayılan örnekleme yöntemi ( SYSTEMve BERNOULLI), tablodaki toplam satır sayısının yüzdesi olarak döndürülecek satır sayısını belirtmenizi gerektirir.

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

Bu, uygun yüzdeyi seçmek için tablodaki kayıt miktarını bilmeyi gerektirir; bu, hızlı bir şekilde bulunamayabilir. Neyse ki, doğrudan döndürülecek satır sayısını belirtmenize izin veren tsm_system_rowsmodül var.

CREATE EXTENSION tsm_system_rows;

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

Bunu ActiveRecord içinde kullanmak için, öncelikle taşıma işleminde uzantıyı etkinleştirin:

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

Sonra fromsorgunun yan tümcesini değiştirin :

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

Bilmiyorum SYSTEM_ROWS örnekleme yöntemi tamamen rastgele olacaktır ya da sadece rastgele sayfadan ilk satırı dönerse.

Bu bilgilerin çoğu Gülçin Yıldırım tarafından yazılan bir 2ndQuadrant blog yazısından alınmıştır .


1

Bu kadar çok cevap gördükten sonra hepsini PostgreSQL (9.6.3) veritabanımda karşılaştırmaya karar verdim. Daha küçük bir 100.000 tablo kullanıyorum ve Model.order'dan ("RANDOM ()") kurtuldum.

10 sütunlu 2.500.000 giriş içeren bir tablo kullanarak eller aşağı kazanan, koşucu yönteminden (ofset) neredeyse 8 kat daha hızlıydı. Bu yöntem, sorunlara neden olabileceğini belirtmek gerekir ki, bunların her biri daha az rastgele aka olacak çünkü aynı anda 1'den fazla sonuç koparmak olduğunu.

25.000.000 satır masamda 100 kez çalışan Pluck kazanır Düzenleme: aslında bu kez dışarı alırsanız döngüde pluck içerir kimliğini basit yineleme kadar hızlı çalışır. Ancak; oldukça fazla RAM gerektirir.

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)

İşte rastgele dışarıda bırakmak için 100.000 satır tablomda 2000 kez çalışan veriler

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

Çok eski bir soru ama:

rand_record = Model.all.shuffle

Rastgele sıralamaya göre sıralanmış bir Kayıt Diziniz var. Mücevherlere veya komut dosyalarına gerek yok.

Bir kayıt istiyorsanız:

rand_record = Model.all.shuffle.first

1
En iyi seçenek değil, çünkü bu tüm kayıtları belleğe yükler. Ayrıca, shuffle.first==.sample
Andrew Rozhenko

0

RoR'da yeniyim ama bunu benim için işe aldım:

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

Dan geldi:

Ruby'de bir diziyi rastgele sıralamak (karıştırmak) nasıl?


4
Bu konuda kötü olan şey, veritabanındaki tüm kartları yükleyecek olmasıdır. Veritabanında yapmak daha verimlidir.
Anton Kuzmin

Ayrıca, dizileri de karıştırabilirsiniz array.shuffle. Her neyse, Card.alltüm kart kayıtlarını belleğe yükleyeceğinden, bahsettiğimiz daha fazla nesnenin verimsizleşmesine dikkat edin.
Thomas Klemm

0

Ne yapmalı?

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

Benim için çok açık


0

Ben Sam örneği örneği Benchmark 4.2.8 raylar kullanarak denedim (rasgele için 1..Category.count koymak, çünkü rastgele 0 alırsa bir hata üretecektir (ActiveRecord :: RecordNotFound: Bulamadım) 'İd' = 0) olan kategori) ve maden:

 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)düzgün görünüyor, ancak büyük tablolar için yavaş çünkü limit1 olsa bile tüm satırları getirmesi ve sıralaması gerekiyor (dahili veritabanında ama Rails değil). MySQL'den emin değilim ama bu Postgres'te oluyor. Burada ve burada daha fazla açıklama .

Büyük tablolar için bir çözüm, araçların olduğu .from("products TABLESAMPLE SYSTEM(0.5)")yerdir . Ancak, çok sayıda satırı filtreleyen koşullarınız varsa, bu çözümün hala yavaş olduğunu görüyorum . Sanırım daha önce tüm satırları getir çünkü0.50.5%WHERETABLESAMPLE SYSTEM(0.5)WHERE koşullar geçerli .

Büyük tablolar için başka bir çözüm (ama çok rastgele değil):

products_scope.limit(sample_size).sample(limit)

nerede sample_sizeolabilir 100(ama çok büyük değil, aksi takdirde yavaştır ve çok fazla bellek tüketir) ve limitolabilir 1. Bu hızlı olmasına rağmen gerçekten rastgele olmasa da, sample_sizeyalnızca kayıtlar içinde rastgele olduğunu unutmayın .

Not: Yukarıdaki yanıtlardaki kıyaslama sonuçları güvenilir değildir (en azından Postgres'te), çünkü 2. sırada çalışan bazı DB sorguları, DB önbelleği sayesinde 1. seferde çalıştırmaktan önemli ölçüde daha hızlı olabilir. Ve ne yazık ki Postgres'te önbelleği devre dışı bırakmanın kolay bir yolu yok.


0

Kullanmanın yanı sıra RANDOM(), bunu bir kapsama da atabilirsiniz:

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

Ya da bunu bir kapsam olarak düşünmüyorsanız, onu bir sınıf yöntemine atın. Şimdi Thing.randomile birlikte çalışıyor Thing.random(n).


0

"Rastgele" nin anlamına ve gerçekte ne yapmak istediğinize bağlı olarak, take olarak yeterli olabilir.

Rastgele "anlamı" ile kastediyorum:

  • Bana pozisyonunu umursamadığım herhangi bir unsur vermek mi demek istiyorsun? o zaman yeterlidir.
  • Şimdi, "bana tekrarlanan deneylerin setten farklı öğeler verebileceği adil bir olasılığı olan herhangi bir öğeyi ver" demek istiyorsanız, "Şans" ı diğer yanıtlarda belirtilen yöntemlerden herhangi biriyle zorlayın.

Örnek olarak, test için örnek veriler yine de rastgele oluşturulmuş olabilir, bu yüzden takeyeterlidir ve hatta dürüst olmak gerekirse first.

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.