找到一个更复杂的Rails查询更便宜的SQL

时间:2015-09-22 07:16:17

标签: sql ruby-on-rails postgresql ruby-on-rails-4 activerecord

Country has_many residencesResidence has_many listed_amenitiesamenities相关联。我需要知道位于residences且ID为1 Country amenities的{​​{1}}有{{4,49,50} {他们可以拥有更多,但他们有拥有所有这三个)。

以下Ruby on Rails(v4.2.4)代码可以解决这个问题:

id_list = [48, 49, 50]
Country.first.
        residences.
        where(id: Residence.
        joins(:listed_amenities).
        where(listed_amenities: {amenity_id: id_list}).
        group('residences.id').
        having("count(listed_amenities.*) = ?", id_list.size).
        pluck(:id)).
        count

它产生以下SQL:

SELECT  "countries".* 
FROM "countries"  
ORDER BY "countries"."id" ASC LIMIT 1

SELECT "residences"."id" 
FROM "residences" 
INNER JOIN "listed_amenities" 
ON "listed_amenities"."residence_id" = "residences"."id" 
WHERE "listed_amenities"."amenity_id" IN (48, 49, 50) 
GROUP BY residences.id 
HAVING count(listed_amenities.*) = 3

SELECT COUNT(*) 
FROM "residences" 
WHERE "residences"."country_id" = $1 
AND "residences"."id" 
IN (51, 59, 60, ...)  [["country_id", 1]]

显然,这是一种解决特定问题的昂贵方法,每个表都有超过1,000,000个条目。

如何以更好/更快的方式在PostgreSQL(9.4.4)上解决这个问题?是否有一条神奇的SQL行可以解决这个问题?

感谢“Get the count of rows count after GROUP BY”我有以下SQL查询来搜索所有residences,但我缺乏将Country连接到它的SQL专有技术:

SELECT count(*) AS ct
FROM  (
   SELECT 1
   FROM   listed_amenities
   WHERE  amenity_id IN (48, 49, 50)
   GROUP  BY residence_id 
   HAVING count(*) = 3
   ) sub;

Ruby on Rails设置:

rails g scaffold Country name
rails g scaffold Residence country:references name:string
rails g scaffold Amenity name:string
rails g scaffold ListedAmenity residence:references amenity:references

应用/模型/ country.rb

class Country < ActiveRecord::Base
  has_many :residences
end

应用/模型/ residences.rb

class Residence < ActiveRecord::Base
  belongs_to :country

  has_many :listed_amenities
  has_many :amenities, through: :listed_amenities
end

应用/模型/ amenities.rb

class Residence < ActiveRecord::Base
  belongs_to :country

  has_many :listed_amenities
  has_many :amenities, through: :listed_amenities
end

应用/模型/ listed_amenities.rb

class ListedAmenity < ActiveRecord::Base
  belongs_to :residence
  belongs_to :amenity
end

1 个答案:

答案 0 :(得分:1)

我认为你可以将其简化为:

Residence.
    joins(:listed_amenities, :country).
    where(listed_amenities: {amenity_id: id_list}).
    where(countries: {id: 1}).
    group('residences.id').
    having("count(listed_amenities.*) = ?", id_list.size)

对于计数,请尝试添加:

    count.count