레일 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) SELECT
users
출발지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 FROM
users
Load ( SELECT 용드로선택 (0.2ms) 사users
출발지users
WHEREusers
.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
효율적인 솔루션을 구축하고 싶은 고객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 }
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
이 보석을 제외한 다른 모든 답변은 대규모 데이터베이스에서 성능이 저하됩니다.
- 빠른 속도로 비용 절감
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
완전.
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
'programing' 카테고리의 다른 글
전용 시스템의 Azure SQL 데이터베이스와 MS SQL Server 비교 (0) | 2023.05.31 |
---|---|
GitHub 풀 요청을 수행하는 방법 (0) | 2023.05.31 |
프로젝트를 열 수 없습니다...프로젝트 파일을 구문 분석할 수 없으므로 열 수 없습니다. (0) | 2023.05.31 |
보기 컨트롤러에 없을 때 UIAertController를 표시하는 방법은 무엇입니까? (0) | 2023.05.26 |
왜 수학을 합니까?라운드(2.5)는 3 대신 2를 반환합니까? (0) | 2023.05.26 |