따라서 Rails 2에서 무작위 레코드를 찾는 몇 가지 예를 찾았습니다. 선호하는 방법은 다음과 같습니다.
Thing.find :first, :offset => rand(Thing.count)
초보자의 무언가이기 때문에 Rails 3의 새로운 찾기 구문을 사용하여 어떻게 구성 할 수 있는지 잘 모르겠습니다.
랜덤 레코드를 찾는 "Rails 3 Way"는 무엇입니까?
따라서 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;
RAND()
또는 RANDOM()
입니다. 감사합니다
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 = 임시 사용; 파일 정렬 사용 ).
반면 쿼리를 두 부분으로 나누고 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에서 실행됩니다.
RAND(id)
것 NOT 다른 임의의 순서로 당신에게 모든 쿼리를 제공합니다. RAND()
쿼리마다 다른 순서를 원하면 사용하십시오 .
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 미만이 소요됩니다.
take
은 LIMIT(1)
쿼리 를 제공 하지만 배열 대신 단일 요소를 반환 하는 함수를 사용 한다는 것입니다. 따라서 우리는 호출 할 필요가 없습니다first
여기 우리는 간다
#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
Couldn't find all Users with 'id': (first, {:offset=>1}) (found 0 results, but was looking for 2)
"RANDOM()"
대신 사용할 수 있습니다 .
이것은 나에게 매우 유용했지만 좀 더 융통성이 필요했기 때문에 이것이 내가 한 일입니다.
사례 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
사례 2 : 여러 개의 무작위 레코드 찾기 (반복 없음) 출처 : 반복 하지 않고
10 개의 무작위 레코드를 찾아야했기 때문에 이것이
컨트롤러 에서 작동하는 것을 기억 합니다.
thing_ids = Thing.find( :all, :select => 'id' ).map( &:id )
@things = Thing.find( (1..10).map { thing_ids.delete_at( thing_ids.size * rand ) } )
이것은 10 개의 무작위 레코드를 찾을 수 있지만, 데이터베이스가 특히 큰 경우 (수백만 개의 레코드) 이것이 이상적이지 않으며 성능이 저하 될 것임을 언급 할 가치가 있습니다. 나에게 충분한 수천 개의 기록을 잘 수행 할 것입니다.
목록에서 항목을 임의로 선택하는 Ruby 방법은 sample
입니다. sample
ActiveRecord를 효율적으로 만들고 싶었고 이전 답변을 바탕으로 다음을 사용했습니다.
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 }
#count
에 대한 DB 호출을 수행합니다 COUNT
. 레코드가 이미로드 된 경우 이는 나쁜 생각 일 수 있습니다. 리 팩터는 #size
대신 사용 #count
해야하는지 또는 레코드가 이미로드되어 있는지를 결정하기 때문에 대신 사용 하는 것 #length
입니다.
Rails 5에서 작동하며 DB에 독립적입니다.
이것은 컨트롤러에서 :
@quotes = Quote.offset(rand(Quote.count - 3)).limit(3)
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
그때...
class Book < ActiveRecord::Base
include Randomable
end
그런 다음 다음을 수행하여 간단히 사용할 수 있습니다.
Books.random
또는
Books.random(3)
ActiveRecord에서 sample ()을 사용할 수 있습니다
예 :
def get_random_things_for_home_page
find(:all).sample(5)
end
출처 : http://thinkingeek.com/2011/07/04/easily-select-random-records-rails/
sample
ActiveRecord에없고 샘플이 배열에 있습니다. api.rubyonrails.org/classes/Array.html#method-i-sample
Oracle을 사용하는 경우
User.limit(10).order("DBMS_RANDOM.VALUE")
산출
SELECT * FROM users ORDER BY DBMS_RANDOM.VALUE WHERE ROWNUM <= 10
많은 데이터 행이있는 테이블을 위해 특별히 설계된 임의 레코드에 대해이 gem을 강력하게 권장합니다.
https://github.com/haopingfan/quick_random_records
이 gem을 제외하고 다른 모든 답변은 큰 데이터베이스에서 제대로 수행되지 않습니다.
4.6ms
총 비용 입니다.User.order('RAND()').limit(10)
비용 733.0ms
.offset
접근 방식은 비용이 245.4ms
완전히.User.all.sample(10)
접근 비용 573.4ms
.참고 : 내 테이블에는 120,000 명의 사용자 만 있습니다. 기록이 많을수록 성능 차이가 더 커집니다.
최신 정보:
550,000 개의 행이있는 테이블에서 수행
Model.where(id: Model.pluck(:id).sample(10))
비용 1384.0ms
gem: quick_random_records
6.4ms
완전히 비용테이블에서 여러 개의 무작위 레코드를 얻는 매우 쉬운 방법입니다. 이렇게하면 2 개의 저렴한 쿼리가 만들어집니다.
Model.where(id: Model.pluck(:id).sample(3))
"3"을 원하는 임의의 레코드 수로 변경할 수 있습니다.
방금 DB에서 무작위 질문을 선택하려는 작은 응용 프로그램을 개발하는이 문제에 부딪 쳤습니다. 나는 사용했다 :
@question1 = Question.where(:lesson_id => params[:lesson_id]).shuffle[1]
그리고 그것은 나를 위해 잘 작동합니다. 더 큰 DB의 성능이 작은 응용 프로그램이므로 어떻게 큰 DB의 성능에 대해 말할 수 없습니다.
shuffle[0]
)