用 arel 中的 union 重写复杂的查询?

发布于 2024-09-15 06:42:01 字数 963 浏览 6 评论 0原文

我有以下模型

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
)

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

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

I have the following models

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

and then i have the following query:

# 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
)

In short: i am looking for all couriers that have a coverage for the given country-code or a coverage with an empty country-code instead (the fallback).

The query works, but i am wondering if there are any better ways to write it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

趴在窗边数星星i 2024-09-22 06:42:01

如果您想保留 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 ]

If you wanted to keep the find_by_sql you could condense the query into:

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 ]
幽蝶幻影 2024-09-22 06:42:01

使用 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)))

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

With arel it seems not very difficult to get something similar:

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)))

This will perform one query that gets the coverages and associated couriers for the given conditions. I do not believe it would be very difficult to adapt this in order to get the intended result.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文