programing

레일 3: 랜덤 레코드 가져오기

muds 2023. 5. 31. 18:33
반응형

레일 3: 랜덤 레코드 가져오기

Rails 2에서 랜덤 레코드를 찾는 몇 가지 예를 찾았습니다. 선호되는 방법은 다음과 같습니다.

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

새로운 사람이기 때문에 레일 3의 새로운 찾기 구문을 사용하여 이것이 어떻게 구성될 수 있는지 잘 모르겠습니다.

그렇다면, 무작위 기록을 찾기 위한 "레일 3의 방법"은 무엇일까요?

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

실제로 레일 3에서는 모든 예제가 작동합니다.하지만 순서를 사용하는 것.RANDOM SQL 이 더 느립니다.

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

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

저는 db가 localhost에 있고 사용자 테이블에 100K 레코드가 조금 넘는 프로젝트(Rails 3.0.15, ruby 1.9.3-p125-perf)를 진행하고 있습니다.

사용.

랜드별 주문()

꽤 느립니다.

User.order("RAND(id)").첫번째

된다

합니다.users출발지users LIMIT 1RAND(ID) 주문 제작사 1

응답하는 데 8~12초걸립니다!!

레일 로그:

Load ( SELECT 용사로드자 (11030.8ms) SELECTusers출발지users 1 랜드별() 한도문() 주▁1 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 |
+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+

인덱스가 사용되지 않음을 확인할 수 있습니다(sys_keys = NULL). 임시 테이블이 생성되고 원하는 값을 가져오려면 추가 패스가 필요합니다(예: 임시 = 임시 사용; filesort 사용).

반면에 쿼리를 두 부분으로 나누고 Ruby를 사용함으로써 응답 시간이 상당히 향상되었습니다.

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

(; 콘솔용으로 제공됨)

레일 로그:

Load () FROM id FROM (25.2ms) SELECT id FROMusersLoad ( SELECT 용드로선택 (0.2ms) 사users출발지usersWHERE users.id LIMIT 1106854 제한 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배 더 빠르게 수행할 수 있습니다.

업데이트:

icant bool이 코멘트에서 지적한 바와 같이 테이블에 삭제된 레코드가 있는 경우 위 솔루션에 결함이 있습니다.

다음과 같은 문제를 해결할 수 있습니다.

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을 만들었습니다.

https://github.com/spilliton/randumb

(edit): 내 보석의 기본 동작은 기본적으로 위와 동일한 접근 방식을 사용하지만, 원한다면 예전 방식을 사용할 수 있는 옵션이 있습니다 :)

게시된 답변의 대부분은 실제로 큰 테이블(100만 개 이상의 행)에서는 제대로 수행되지 않습니다.랜덤 주문은 빠르게 몇 초가 걸리고 테이블에서 카운트하는 데도 꽤 오래 걸립니다.

이 상황에서 나에게 잘 맞는 해결책은 사용하는 것입니다.RANDOM()을 사용하여: 여기서 조:

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: 1: 무작위 기록 찾기 site:turk .
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개의 레코드를 찾을 수 있지만, 데이터베이스가 특히 큰 경우(수백만 개의 레코드) 이는 이상적이지 않으며 성능이 저하된다는 점을 언급할 가치가 있습니다.저에게 충분했던 몇 천 장의 음반까지 잘 연주할 것입니다.

은 목에서항무목작로선루방은입니다.sample효율적인 솔루션을 구축하고 싶은 고객sampleActiveRecord의 경우 이전 답변을 기반으로 다음을 사용했습니다.

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 }

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에서 샘플()을 사용할 수 있습니다.

예.

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

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

Oracle을 사용하는 경우

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

산출량

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

데이터 행이 많은 테이블용으로 특별히 설계된 랜덤 레코드에 대해 이 보석을 강력하게 권장합니다.

https://github.com/haopingfan/quick_random_records

이 보석을 제외한 다른 모든 답변은 대규모 데이터베이스에서 성능이 저하됩니다.

  1. 빠른 속도로 비용 절감4.6ms완전.

여기에 이미지 설명 입력

  1. 으로 받아들여진User.order('RAND()').limit(10)이 용비 들다733.0ms.

여기에 이미지 설명 입력

  1. 그자리의 offset 비용 근접비245.4ms완전.

여기에 이미지 설명 입력

  1. 그자리의 User.all.sample(10) 비용 근접비573.4ms.

여기에 이미지 설명 입력

참고: 내 테이블의 사용자 수는 120,000명입니다.더 많은 레코드를 보유할수록 성능의 차이는 더 커질 것입니다.


업데이트:

550,000개의 행이 있는 테이블에서 수행

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

여기에 이미지 설명 입력

  1. gem: quick_random_records유일한 비용6.4ms완전.

여기에 이미지 설명 입력

RDBMS에 의한 무작위 정렬은 수백만 개의 레코드가 있는 경우 상당히 비쌀 수 있습니다.이를 단순화하기 위해 정렬된 레코드 수를 다음과 같이 제한할 수 있습니다(PostgreSQL 구문):

module ActiveRecord
  class Base
    def self.sample
      where(
        "id >= TRUNC(RANDOM() * (SELECT MAX(id) FROM #{table_name}) + 1)"
      ).order(:id).first
    end
  end
end

그리고 나서.User.sample

이는 ID 균일 분포의 경우 더 랜덤하게 작동합니다.

테이블에서 여러 랜덤 레코드를 쉽게 가져올 수 있습니다.이것은 2개의 저렴한 쿼리를 만듭니다.

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

"3"을 원하는 랜덤 레코드 수로 변경할 수 있습니다.

저는 제 DB에서 무작위 질문을 선택하고자 하는 작은 애플리케이션을 개발하는 과정에서 이 문제에 부딪혔습니다.사용:

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

그리고 그것은 저에게 잘 작동하고 있습니다.저는 이것이 단지 작은 애플리케이션이기 때문에 더 큰 DB의 성능에 대해 말할 수 없습니다.

언급URL : https://stackoverflow.com/questions/5342270/rails-3-get-random-record

반응형