복붙노트

[RUBY-ON-RAILS] 사용 중단 경고 : 위험한 쿼리 방법 :> = 5.2 액티브에서 임의의 기록

RUBY-ON-RAILS

사용 중단 경고 : 위험한 쿼리 방법 :> = 5.2 액티브에서 임의의 기록

지금까지 데이터베이스에서 임의의 기록을 얻을 수있는 "일반적인"방법이있다 :

# Postgress
Model.order("RANDOM()").first 

# MySQL
Model.order("RAND()").first

레일 5.2에서이 일 때, 다음과 같은 사용 중단 경고를 보여줍니다

나는 Arel 정말 익숙하지 않다, 그래서 나는 반드시이 문제를 해결하는 올바른 방법 일 것입니다 무슨 아닙니다.

해결법

  1. ==============================

    1.당신에 의해 주문을 계속 사용하려는 경우) (랜덤 후 바로 사용 중단 경고가 제안처럼 Arel.sql에 포장하여 안전 선언

    당신에 의해 주문을 계속 사용하려는 경우) (랜덤 후 바로 사용 중단 경고가 제안처럼 Arel.sql에 포장하여 안전 선언

    Model.order(Arel.sql('random()')).first
    

    임의의 행을 선택하고 그들은 모두 장점과 단점을 가지고하는 방법이 많이 있습니다하지만 그런 당신이 루비의 배열과 일치해야 할 순서를 필요로 할 때와 같이 절대적으로 순서대로 SQL의 조각을 사용해야 번 (있다 Arel.sql를 사용하여 주위를 얻을 수 있도록 데이터베이스에 이르기까지 엔드 표현)이 제한 도구에 대해 알아야 할 우리 모두 필요하다 "속성 만"때 ... 큰 경우를 얻을.

    편집 됨 : 샘플 코드는 닫는 괄호가 없습니다.

  2. ==============================

    2.나는이 솔루션의 팬입니다 :

    나는이 솔루션의 팬입니다 :

    Model.offset(rand(Model.count)).first
    
  3. ==============================

    3.많은 레코드, 그리고 많은하지 삭제 된 레코드로,이보다 효율적으로 할 수있다. 기본 범위는 조인을 사용하기 때문에 내 경우에는 내가 .unscoped 사용해야합니다. 모델 같은 기본 범위를 사용하지 않는 경우, 당신은이 나타나는 곳마다이 .unscoped 생략 할 수 있습니다.

    많은 레코드, 그리고 많은하지 삭제 된 레코드로,이보다 효율적으로 할 수있다. 기본 범위는 조인을 사용하기 때문에 내 경우에는 내가 .unscoped 사용해야합니다. 모델 같은 기본 범위를 사용하지 않는 경우, 당신은이 나타나는 곳마다이 .unscoped 생략 할 수 있습니다.

    Patient.unscoped.count #=> 134049
    
    class Patient
      def self.random
        return nil unless Patient.unscoped.any?
        until @patient do
          @patient = Patient.unscoped.find rand(Patient.unscoped.last.id)
        end
        @patient
      end
    end
    
    #Compare with other solutions offered here in my use case
    
    puts Benchmark.measure{10.times{Patient.unscoped.order(Arel.sql('RANDOM()')).first }}
    #=>0.010000   0.000000   0.010000 (  1.222340)
    Patient.unscoped.order(Arel.sql('RANDOM()')).first
    Patient Load (121.1ms)  SELECT  "patients".* FROM "patients"  ORDER BY RANDOM() LIMIT 1
    
    puts Benchmark.measure {10.times {Patient.unscoped.offset(rand(Patient.unscoped.count)).first }}
    #=>0.020000   0.000000   0.020000 (  0.318977)
    Patient.unscoped.offset(rand(Patient.unscoped.count)).first
    (11.7ms)  SELECT COUNT(*) FROM "patients"
    Patient Load (33.4ms)  SELECT  "patients".* FROM "patients"  ORDER BY "patients"."id" ASC LIMIT 1 OFFSET 106284
    
    puts Benchmark.measure{10.times{Patient.random}}
    #=>0.010000   0.000000   0.010000 (  0.148306)
    
    Patient.random
    (14.8ms)  SELECT COUNT(*) FROM "patients"
    #also
    Patient.unscoped.find rand(Patient.unscoped.last.id)
    Patient Load (0.3ms)  SELECT  "patients".* FROM "patients"  ORDER BY "patients"."id" DESC LIMIT 1
    Patient Load (0.4ms)  SELECT  "patients".* FROM "patients" WHERE "patients"."id" = $1 LIMIT 1  [["id", 4511]]
    

    우리가 임의의 ID를 얻을 단지 하나의 레코드에 발견 할 랜드 ()를 사용하고 있기 때문에 이것에 대한 이유입니다. 그러나 더 큰 삭제 행수 일수록 while 루프가 여러 번 실행된다 (ID를 생략). 그것은 잔인한 수도 있지만 62 %의 성능 증가 및 더 높은 경우는 결코 삭제 행 가치가있을 수 있습니다. 테스트는 사용 사례에 대한 더 나은 경우.

  4. from https://stackoverflow.com/questions/48897070/deprecation-warning-dangerous-query-method-random-record-in-activerecord-5 by cc-by-sa and MIT license