【问题标题】:rewrite complicated query with union in arel?用arel中的联合重写复杂的查询?
【发布时间】:2010-08-19 13:35:00
【问题描述】:

我有以下型号

class Courier < ActiveRecord::Base
  has_many :coverages
end

class Coverage < ActiveRecord::Base
  belongs_to :courier
  belongs_to :country_code
end

class CountryCode < ActiveRecord::Base      
end

然后我有以下查询:

# could i translate this into cleaner arel?
result = Courier.find_by_sql(<<SQL
          select * from
          (
            select cc.*, cv.rate from couriers cc, coverages cv
            where cv.country_code_id=#{country.id} and cv.courier_id=cc.id
          union
            select cc.*, cv.rate from couriers cc, coverages cv
            where cv.country_code_id is null
              and cv.courier_id=cc.id
              and cv.courier_id not in (select courier_id from coverages where country_code_id=#{country.id})
          ) as foo order by rate asc
SQL
)

简而言之:我正在寻找覆盖给定国家代码或覆盖空国家代码的所有快递公司(后备)。

查询有效,但我想知道是否有更好的方法来编写它?

【问题讨论】:

标签: ruby-on-rails-3 arel


【解决方案1】:

如果您想保留find_by_sql,您可以将查询压缩为:

result = Courier.find_by_sql [
  "SELECT cc.*, cv.rate
  FROM couriers cc inner join coverages cv on cv.courier_id = cc.id
  WHERE cv.country_code_id = ?
    OR (cv.country_code_id is null AND cv.courier_id NOT IN (SELECT courier_id FROM coverages WHERE country_code_id= ? ))
  ORDER BY cv.rate asc", country.id, country.id ]

【讨论】:

  • 谢谢,但您的查询与我的查询不同。工会的存在是有原因的。如果没有所需国家代码的快递员,我只想找到国家代码等于 nil 的快递员。
  • 啊,好的,这样一个 Courier 可以通过 Coverages 标记多个国家代码吗?不管怎样,我认为你根本不需要工会。您可以只使用 OR 并将子查询合并为一个 SELECT。我会尝试用 find_by_sql 更新我的答案,然后独立思考 arel
【解决方案2】:

使用 arel 似乎不太难得到类似的东西:

country_code = ...
c=Courier.arel_table
cv=Coverage.arel_table    
courier_ids_with_country_code= Coverage.select(:courier_id).where(:country_code=>country_code)
coverage_ids_and_condition= Coverage.select(:id)
                                    .where(cv[:country_code]
                                               .eq(nil)
                                               .and(cv[:courier_id]
                                                          .in(courier_ids_with_country_code)))
coverage_ids_with_country_code= Coverage.select(:id)
                                        .where(:country_code=>country_code)

coverage_union_joined_with_couriers = Coverage.include(:courier)
                                              .where(cv[:id]
                                                     .in(coverage_ids_with_country_code
                                                         .union(coverage_ids_and_condition)))

这将执行一个查询,以获取给定条件的覆盖范围和关联的快递员。我不认为为了得到预期的结果而调整它会很困难。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多