복붙노트

[SQL] 협회가 존재하지 않는다면 확인 범위를 레일

SQL

협회가 존재하지 않는다면 확인 범위를 레일

나는 특정 관련이없는 모든 레코드를 반환 범위를 서면으로보고하고있다.

Vo.rb

class Foo < ActiveRecord::Base    
  has_many :bars
end

Bar.rb

class Bar < ActiveRecord::Base    
  belongs_to :foo
end

나는 어떤 줄이없는 식품을 모두 찾을 수있는 범위를 원한다. 그것은 조인 사용하여 연결을 가지고있는 사람들을 쉽게 찾을 수 있습니다,하지만 난 반대를 할 수있는 방법을 발견하지 않았습니다.

해결법

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

    1.(4)이 너무 쉽게이 만드는 레일 :)

    (4)이 너무 쉽게이 만드는 레일 :)

    Foo.where.not(id: Bar.select(:foo_id).uniq)
    

    이 jdoe에의 대답과 같은 쿼리를 출력

    SELECT "foos".* 
    FROM "foos" 
    WHERE "foos"."id" NOT IN (
      SELECT DISTINCT "bars"."foo_id"
      FROM "bars" 
    )
    

    그리고 범위로 :

    scope :lonely, -> { where.not(id: Bar.select(:item_id).uniq) }
    
  2. ==============================

    2.나는 100 음식과 9900 바있다. 식품 각 99 100 바 있고, 그 중 하나는 아무도 없습니다.

    나는 100 음식과 9900 바있다. 식품 각 99 100 바 있고, 그 중 하나는 아무도 없습니다.

    Foo.left_outer_joins(:bars).where(bars: { foo_id: nil })
    

    하나 개의 SQL 쿼리를 생성합니다 :

    Foo Load (2.3ms)  SELECT  "foos".* FROM "foos" LEFT OUTER JOIN "bars" ON "bars"."foo_id" = "foos"."id" WHERE "bars"."foo_id" IS NULL
    

    없이 바로 한 푸를 반환

    현재 허용 대답 Foo.where.not (ID : Bar.select (: foo_id) .uniq)가 작동하지 않습니다. 그것은 두 개의 SQL 쿼리를 생산하고있다 :

    Bar Load (8.4ms)  SELECT "bars"."foo_id" FROM "bars"
    Foo Load (0.3ms)  SELECT  "foos".* FROM "foos" WHERE ("foos"."id" IS NOT NULL)
    

    이는 모든 FOOS가 null가 아닌 ID를 가지고 있기 때문에 모든 FOOS를 반환합니다.

    그것은 Foo.where.not로 변경해야하는 하나 개의 쿼리로 줄이고 우리의 푸를 찾기 위해 (: Bar.pluck (ID foo_id) .uniq)하지만 벤치 마크에서 성능이 저하됩니다

    require 'benchmark/ips'
    require_relative 'config/environment'
    
    Benchmark.ips do |bm|
      bm.report('left_outer_joins') do
        Foo.left_outer_joins(:bars).where(bars: { foo_id: nil })
      end
    
      bm.report('where.not') do
        Foo.where.not(id: Bar.pluck(:foo_id).uniq)
      end
    
      bm.compare!
    end
    
    Warming up --------------------------------------
        left_outer_joins     1.143k i/100ms
               where.not     6.000  i/100ms
    Calculating -------------------------------------
        left_outer_joins     13.659k (± 9.0%) i/s -     68.580k in   5.071807s
               where.not     70.856  (± 9.9%) i/s -    354.000  in   5.057443s
    
    Comparison:
        left_outer_joins:    13659.3 i/s
               where.not:       70.9 i/s - 192.77x  slower
    
  3. ==============================

    3.foo.rb에서

    foo.rb에서

    class Foo < ActiveRecord::Base    
      has_many :bars
      scope :lonely, lambda { joins('LEFT OUTER JOIN bars ON foos.id = bars.foo_id').where('bars.foo_id IS NULL') }
    end
    
  4. ==============================

    4.나는 복잡한 쿼리를 구축 squeel 보석을 사용하는 것을 선호합니다. 그것은 같은 마법과 액티브를 확장 :

    나는 복잡한 쿼리를 구축 squeel 보석을 사용하는 것을 선호합니다. 그것은 같은 마법과 액티브를 확장 :

    Foo.where{id.not_in Bar.select{foo_id}.uniq}
    

    그 다음 쿼리를 작성합니다 :

    SELECT "foos".* 
    FROM "foos" 
    WHERE "foos"."id" NOT IN (
      SELECT DISTINCT "bars"."foo_id"
      FROM "bars" 
    )
    

    그래서,

    # in Foo class
    scope :lonely, where{id.not_in Bar.select{foo_id}.uniq}
    

    당신이 요청 된 범위를 구축하는 데 사용할 수있는 것입니다.

  5. ==============================

    5.NOT 사용하면 빠르게 할 수있는 LIMIT-에드 하위 쿼리와 함께 존재합니다

    NOT 사용하면 빠르게 할 수있는 LIMIT-에드 하위 쿼리와 함께 존재합니다

    SELECT foos.* FROM foos
    WHERE NOT EXISTS (SELECT id FROM bars WHERE bars.foo_id = foos.id LIMIT 1);
    

    액티브로 (> = 4.0.0)

    Foo.where.not(Bar.where("bars.foo_id = foos.id").limit(1).arel.exists)
    
  6. from https://stackoverflow.com/questions/10355002/rails-scope-to-check-if-association-does-not-exist by cc-by-sa and MIT license