2010-08-19 29 views
4

我有以下型號用聯合在arel中重寫複雜的查詢?

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 
) 

總之:我正在尋找具有針對特定國家代碼或覆蓋所有快遞用空白的國家代碼覆蓋(後備)。

該查詢的工作原理,但我想知道是否有更好的方法來寫它?

+0

我有一個類似的問題:http://stackoverflow.com/questions/4522746/how-would-i-join-to-a-subselect-a-scope-using-rails-3-and-arel – 2011-01-06 00:18:34

回答

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 ] 
+0

謝謝你,但你的查詢不會做我的工作。工會在那裏是有原因的。我只想找到國家代碼等於零的快遞員,如果沒有通緝快遞員的國家代碼。 – nathanvda 2011-12-30 23:10:44

+0

嗯,好的,這樣一個信使可以通過Coverages標記多個國家代碼嗎?無論哪種方式,我認爲你根本不需要工會。你可以使用OR,並將子查詢放入一個'SELECT'中。我會嘗試用find_by_sql更新我的答案,然後獨立思考arel – jstim 2011-12-31 00:08:56

1

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

這將執行一個查詢獲取給定條件的覆蓋率和相關信使。我不相信爲了達到預期的結果而適應這一點將是非常困難的。