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
. *에서 선택users
1
응답 하는 데 8 초 에서 12 초가 걸립니다 !!
레일스 로그 :
사용자로드 (11030.8ms) 선택
users
. *users
RAND () 제한 순서 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) SELECTusers
. * FROMusers
WHEREusers
.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:
- quick_random_records only cost
4.6ms
totally.
- the accepted answer
User.order('RAND()').limit(10)
cost733.0ms
.
- the
offset
approach cost245.4ms
totally.
- the
User.all.sample(10)
approach cost573.4ms
.
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
Model.where(id: Model.pluck(:id).sample(10))
cost1384.0ms
gem: quick_random_records
only cost6.4ms
totally
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
'development' 카테고리의 다른 글
힘내 숨김 : "더러운 작업 트리에는 적용 할 수 없습니다. 변경 사항을 준비하십시오" (0) | 2020.06.30 |
---|---|
왜 ~ True가 -2가됩니까? (0) | 2020.06.30 |
Selenium Webdriver 2 Python에서 현재 URL을 어떻게 얻습니까? (0) | 2020.06.30 |
라디오 버튼에서 "라벨"사용 (0) | 2020.06.30 |
문자열을 널 입력 가능 유형으로 변환 (int, double 등) (0) | 2020.06.30 |