переписать сложный запрос с объединением в арель? - PullRequest
4 голосов
/ 19 августа 2010

У меня есть следующие модели

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
)

Короче говоря: я ищу всех курьеров, которые имеют покрытие для данногокод страны или покрытие с пустым кодом страны (запасной вариант).

Запрос работает, но мне интересно, есть ли лучшие способы написать его?

Ответы [ 2 ]

1 голос
/ 08 февраля 2013

С арелом, кажется, не очень сложно получить что-то похожее:

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

Это выполнит один запрос, который получит покрытия и связанных курьеров для заданных условий. Я не верю, что было бы очень трудно адаптировать это, чтобы получить намеченный результат.

1 голос
/ 30 декабря 2011

Если вы хотите сохранить 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 ]
...