【问题标题】:Find a cheaper SQL for a complicated Rails query为复杂的 Rails 查询找到更便宜的 SQL
【发布时间】:2015-09-22 07:16:16
【问题描述】:

一个Country has_many residences 和一个Residence has_many listed_amenities 链接到amenities。我需要知道有多少residences 位于Country 中,ID 为 1,amenities 的 ID 为 [48, 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

app/models/country.rb

class Country < ActiveRecord::Base
  has_many :residences
end

app/models/residences.rb

class Residence < ActiveRecord::Base
  belongs_to :country

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

app/models/amenities.rb

class Residence < ActiveRecord::Base
  belongs_to :country

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

app/models/listed_amenities.rb

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

【问题讨论】:

  • 您的代码导致 3 个查询?

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


【解决方案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

【讨论】:

  • 这会产生residences 的列表,但我需要计数。将.count 方法添加到它不起作用。
  • 我需要关于错误消息的提示;)
  • ... 529042=&gt;3, 529050=&gt;3, 529062=&gt;3, 529064=&gt;3, 529067=&gt;3, 529070=&gt;3, 529071=&gt;3, 529077=&gt;3, 529082=&gt;3, 530547=&gt;3}
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-25
  • 1970-01-01
  • 2011-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多