Rails ActiveRecord加入查询条件

时间:2013-08-22 17:51:58

标签: sql ruby-on-rails activerecord

我有以下SQL查询:

SELECT campaigns.* , campaign_countries.points, offers.image
FROM campaigns
JOIN campaign_countries ON campaigns.id = campaign_countries.campaign_id
JOIN countries ON campaign_countries.country_id = countries.id
JOIN offers ON campaigns.offer_id = offers.id
WHERE countries.code = 'US'

这非常有效。我希望它的rails活动记录版本有些像:

Campaign.includes(campaign_countries: :country).where(countries: {code: "US"})

上面的代码运行或多或少正确的查询(没有尝试包含商品表),返回的问题结果是Campaign对象的集合,所以显然它不包括Points

我的表是:

campaigns --HAS_MANY--< campaign_countries --BELONGS_TO--< countries
campaigns --BELONGS_TO--> offers

有关编写此SQL版AR版本的建议吗?我不想在我的代码中使用SQL语句。

3 个答案:

答案 0 :(得分:1)

我知道如何在没有SQL的情况下工作,但肯定是穷人的解决方案:

在我的控制器中我有:

campaigns = Campaign.includes(campaign_countries: :country).where(countries: {code: country.to_s})
render :json => campaigns.to_json(:country => country)
在广告系列模型中

  def points_for_country country
    CampaignCountry.joins(:campaign, :country).where(countries: {code: country}, campaigns: {id: self.id}).first
  end

  def as_json options={}
    json = {
      id: id,
      cid: cid, 
      name: name,
      offer: offer,
      points_details: options[:country] ? points_for_country(options[:country]) : ""
    }
  end

并在campaign_countries模型中:

  def as_json options={}
    json = {
        face_value: face_value,
        actual_value: actual_value,
        points: points
    }
  end

为什么这不是好的解决方案?因为它会调用太多查询: 1.它在执行第一次加入时调用查询以获取特定于国家/地区的广告系列列表 2.对于在第一个查询中找到的每个广告系列,它会在campaign_countries表上再调用一次查询,以获取该广告系列和国家/地区的点数。

这是糟糕的,坏的和坏的解决方案。有什么建议可以改善这个吗?

答案 1 :(得分:0)

如果您有广告系列,则可以使用campaign.campaign_countries获取关联的广告系列国家,并从中获取积分。

> campaign.campaign_countries.map(&:points)
=> [1,2,3,4,5]

同样,您将能够从优惠关系中获取图像。

编辑:

好的,我想现在我知道发生了什么事。您可以将joinsselect一起使用,以获取包含连接表中附加字段的对象。

cs = Campaign.joins(campaign_countries: :country).joins(:offers).select('campaigns.*, campaign_countries.points, offers.image').where(countries: {code: "US"})

您可以在Campaign对象

上按名称引用其他字段
cs.first.points
cs.first.image

但请确保,其他列名称不会与某些主表字段或对象方法重叠。

编辑2:

经过一些研究后,我得出结论,我的第一个版本对于这个案例实际上是正确的。我将以自己的控制台为例。

> u = User.includes(:orders => :cart).where(:carts => { :id => [5168, 5167] }).first
> u.orders.length # no query is performed
=> 2
> u.orders.count # count query is performed
=> 5

因此,当您在国家/地区使用includes条件时,campaign_countries仅存储符合您条件的campaign_countries

答案 2 :(得分:0)

试试这个:

Campaign.joins( [{ :campaign_countries =>  :countries}, :offers]).where('`countries`.`code` = ?', "US")