[ruby-on-rails] 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

로부터 얻어 812 초응답 에서 !!

레일스 로그 :

사용자로드 (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 = 임시 사용; 파일 정렬 사용 ).

반면 쿼리를 두 부분으로 나누고 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

사례 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 개의 무작위 레코드를 찾을 수 있지만, 데이터베이스가 특히 큰 경우 (수백만 개의 레코드) 이것이 이상적이지 않으며 성능이 저하 될 것임을 언급 할 가치가 있습니다. 나에게 충분한 수천 개의 기록을 잘 수행 할 것입니다.