따라서 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 = 임시 사용; 파일 정렬 사용 ).
반면 쿼리를 두 부분으로 나누고 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
. * 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
사례 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 개의 무작위 레코드를 찾을 수 있지만, 데이터베이스가 특히 큰 경우 (수백만 개의 레코드) 이것이 이상적이지 않으며 성능이 저하 될 것임을 언급 할 가치가 있습니다. 나에게 충분한 수천 개의 기록을 잘 수행 할 것입니다.