【问题标题】:Rails ActiveRecord Join Query With conditionsRails ActiveRecord 加入查询条件
【发布时间】:2013-08-22 17:51:58
【问题描述】:

我有以下 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 对象的集合,因此显然它不包含积分

我的桌子是:

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

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

【问题讨论】:

    标签: sql ruby-on-rails activerecord


    【解决方案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 表上再调用一个查询以获取该活动和国家/地区的积分。

    这是一个糟糕的、糟糕的和糟​​糕的解决方案。有什么改进的建议吗?

    【讨论】:

      【解决方案2】:

      如果您有活动,您可以使用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

      【讨论】:

      • 我认为我的回答现在应该让你满意了,但我认为这不是很Railish。
      • 现在(在第二次编辑之后)我很确定这就是你要找的东西
      • 我正在尝试您的第二次编辑,我不确定它是否有效,但看起来像 cs.first.points cs.first.image 如果这有效,那么它会很好。
      • 太棒了!!!第二次编辑我没有尝试,我想它会调用多个查询,但是第一次编辑真的很好。当然不是 100% Railish 方式,但我想应该没问题,因为它涉及非常基本的 SQL,应该与任何后端数据库系统兼容。我还关心一件事,就是这条单行语句的性能。但我想我将来会调查它:) 谢谢!
      【解决方案3】:

      试试这个:

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

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-20
      • 1970-01-01
      相关资源
      最近更新 更多