development

Rails 3 : 랜덤 레코드 받기

big-blog 2020. 6. 30. 08:00
반응형

Rails 3 : 랜덤 레코드 받기


따라서 Rails 2에서 무작위 레코드를 찾는 몇 가지 예를 찾았습니다. 선호하는 방법은 다음과 같습니다.

Thing.find :first, :offset => rand(Thing.count)

초보자의 무언가이기 때문에 Rails 3의 새로운 찾기 구문을 사용하여 어떻게 구성 할 수 있는지 잘 모르겠습니다.

랜덤 레코드를 찾는 "Rails 3 Way"는 무엇입니까?


Thing.first(:order => "RANDOM()") # For MySQL :order => "RAND()", - thanx, @DanSingerman
# Rails 3
Thing.order("RANDOM()").first

또는

Thing.first(:offset => rand(Thing.count))
# Rails 3
Thing.offset(rand(Thing.count)).first

실제로 Rails 3에서는 모든 예제가 작동합니다. 그러나 RANDOM큰 테이블의 경우 순서를 사용하는 것이 상당히 느리지 만 더 SQL 스타일

UPD. 인덱스 열에서 다음과 같은 트릭을 사용할 수 있습니다 (PostgreSQL 구문).

select * 
from my_table 
where id >= trunc(
  random() * (select max(id) from my_table) + 1
) 
order by id 
limit 1;

db가 localhost에 있고 users 테이블에 100K 개 이상의 레코드 가있는 프로젝트 ( Rails 3.0.15, ruby ​​1.9.3-p125-perf )를 작업 중 입니다.

사용

RAND ()로 주문

꽤 느리다

User.order ( "RAND (id)"). first

된다

RAND (ID) 제한으로 주문 users. *에서 선택 users1

응답 하는 8 에서 12 초가 걸립니다 !!

레일스 로그 :

사용자로드 (11030.8ms) 선택 users. * usersRAND () 제한 순서 1

MySQL의 설명에서

+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows   | Extra                           |
+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+
|  1 | SIMPLE      | users | ALL  | NULL          | NULL | NULL    | NULL | 110165 | Using temporary; Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+

인덱스가 사용되지 않고 ( possible_keys = NULL ) 임시 테이블이 작성되며 원하는 값을 가져 오기 위해 추가 패스가 필요함을 알 수 있습니다 ( extra = Using temporary; Using filesort ).

반면 쿼리를 두 부분으로 나누고 Ruby를 사용하면 응답 시간이 상당히 향상됩니다.

users = User.scoped.select(:id);nil
User.find( users.first( Random.rand( users.length )).last )

(; 콘솔 사용에는 없음)

레일스 로그 :

사용자로드 (25.2ms) SELECT id FROM users사용자로드 (0.2ms) SELECT users. * FROM usersWHERE users. id= 106854 제한 1

그리고 mysql의 설명은 이유를 증명합니다 :

+----+-------------+-------+-------+---------------+--------------------------+---------+------+--------+-------------+
| id | select_type | table | type  | possible_keys | key                      | key_len | ref  | rows   | Extra       |
+----+-------------+-------+-------+---------------+--------------------------+---------+------+--------+-------------+
|  1 | SIMPLE      | users | index | NULL          | index_users_on_user_type | 2       | NULL | 110165 | Using index |
+----+-------------+-------+-------+---------------+--------------------------+---------+------+--------+-------------+

+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | users | const | PRIMARY       | PRIMARY | 4       | const |    1 |       |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+

이제 인덱스와 기본 키만 사용할 수 있으며 약 500 배 빠르게 작업을 수행 할 수 있습니다!

최신 정보:

주석에서 icantbecool이 지적한 것처럼 위의 솔루션은 테이블에 삭제 된 레코드가 있으면 결함이 있습니다.

그 해결 방법은 다음과 같습니다.

users_count = User.count
User.scoped.limit(1).offset(rand(users_count)).first

두 개의 쿼리로 변환됩니다

SELECT COUNT(*) FROM `users`
SELECT `users`.* FROM `users` LIMIT 1 OFFSET 148794

약 500ms에서 실행됩니다.


Postgres를 사용하는 경우

User.limit(5).order("RANDOM()")

MySQL을 사용하는 경우

User.limit(5).order("RAND()")

두 경우 모두 Users 테이블에서 무작위로 5 개의 레코드를 선택합니다. 다음은 콘솔에 표시되는 실제 SQL 쿼리입니다.

SELECT * FROM users ORDER BY RANDOM() LIMIT 5

큰 테이블에서 더 잘 수행하고 관계와 범위를 연결할 수 있도록 레일 3 gem을 만들었습니다.

https://github.com/spilliton/randumb

(편집) : 내 보석의 기본 동작은 기본적으로 위와 동일한 접근 방식을 사용하지만 원하는 경우 이전 방식을 사용할 수 있습니다. :)


게시 된 많은 답변이 실제로 큰 테이블 (1 + 백만 행)에서 제대로 수행되지 않습니다. 임의 순서는 몇 초가 걸리고 테이블에서 카운트를 수행하는 데에도 시간이 오래 걸립니다.

이 상황에서 나에게 잘 맞는 해결책 RANDOM()은 where 조건과 함께 사용 하는 것입니다.

Thing.where('RANDOM() >= 0.9').take

백만 개가 넘는 행이있는 테이블에서이 쿼리는 일반적으로 2ms 미만이 소요됩니다.


여기 우리는 간다

레일스 웨이

#in your initializer
module ActiveRecord
  class Base
    def self.random
      if (c = count) != 0
        find(:first, :offset =>rand(c))
      end
    end
  end
end

용법

Model.random #returns single random object

아니면 두번째 생각은

module ActiveRecord
  class Base
    def self.random
      order("RAND()")
    end
  end
end

용법:

Model.random #returns shuffled collection

이것은 나에게 매우 유용했지만 약간 더 많은 유연성이 필요했기 때문에 이것이 내가 한 일입니다.

사례 1 : 하나의 무작위 레코드 소스 찾기 : 트레버 터크 사이트
Thing.rb 모델에 추가

def self.random
    ids = connection.select_all("SELECT id FROM things")
    find(ids[rand(ids.length)]["id"].to_i) unless ids.blank?
end

그런 다음 컨트롤러에서 다음과 같이 호출 할 수 있습니다

@thing = Thing.random

Case2: Finding multiple random records(no repeats)source:can't remember
I needed to find 10 random records with no repeats so this is what i found worked
In your controller:

thing_ids = Thing.find( :all, :select => 'id' ).map( &:id )
@things = Thing.find( (1..10).map { thing_ids.delete_at( thing_ids.size * rand ) } )

This will find 10 random records, however it is worth mentioning that if the database is particularly large(millions of records), this would not be ideal, and performance will be hampered. Is will perform well up to a few thousand records which was sufficient for me.


The Ruby method for randomly picking an item from a list is sample. Wanting to create an efficient sample for ActiveRecord, and based on the previous answers, I used:

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

I put this in lib/ext/sample.rb and then load it with this in config/initializers/monkey_patches.rb:

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

Works in Rails 5 and is DB agnostic:

This in your controller:

@quotes = Quote.offset(rand(Quote.count - 3)).limit(3)

You can, of course, put this in a concern as shown here.

app/models/concerns/randomable.rb

module Randomable
  extend ActiveSupport::Concern

  class_methods do
    def random(the_count = 1)
      records = offset(rand(count - the_count)).limit(the_count)
      the_count == 1 ? records.first : records
    end
  end
end

then...

app/models/book.rb

class Book < ActiveRecord::Base
  include Randomable
end

Then you can use simply by doing:

Books.random

or

Books.random(3)

You can use sample() in ActiveRecord

E.g.

def get_random_things_for_home_page
  find(:all).sample(5)
end

Source: http://thinkingeek.com/2011/07/04/easily-select-random-records-rails/


If using Oracle

User.limit(10).order("DBMS_RANDOM.VALUE")

Output

SELECT * FROM users ORDER BY DBMS_RANDOM.VALUE WHERE ROWNUM <= 10

Strongly Recommend this gem for random records, which is specially designed for table with lots of data rows:

https://github.com/haopingfan/quick_random_records

All other answers perform badly with large database, except this gem:

  1. quick_random_records only cost 4.6ms totally.

enter image description here

  1. the accepted answer User.order('RAND()').limit(10) cost 733.0ms.

enter image description here

  1. the offset approach cost 245.4ms totally.

enter image description here

  1. the User.all.sample(10) approach cost 573.4ms.

enter image description here

Note: My table only has 120,000 users. The more records you have, the more enormous the difference of performance will be.


UPDATE:

Perform on table with 550,000 rows

  1. Model.where(id: Model.pluck(:id).sample(10)) cost 1384.0ms

enter image description here

  1. gem: quick_random_records only cost 6.4ms totally

enter image description here


A very easy way to get multiple random records from the table. This makes 2 cheap queries.

Model.where(id: Model.pluck(:id).sample(3))

You can change the "3" to the number of random records you want.


I just ran into this issue developing a small application where I wanted to select a random question from my DB. I used:

@question1 = Question.where(:lesson_id => params[:lesson_id]).shuffle[1]

And it's working well for me. I can't speak on how the performance for larger DBs since this is just a small application.

참고URL : https://stackoverflow.com/questions/5342270/rails-3-get-random-record

반응형