如果其中一个has_many相关实体具有值为x的属性,则排除对象

时间:2014-10-16 10:24:27

标签: ruby-on-rails ruby activerecord

如果其中一个关联数据的属性x的值为'a',我就会发现排除数据的问题。

示例:

class Order < ActiveRecord::Base
  has_many :items
end

class Item < ActiveRecord::Base
  belongs_to :order

  validate_presence_of :status
end

查询应返回具有 status ='付费'的项目的所有订单(状态!='付费')。

由于1:n关联,订单可以包含多个商品。其中一个Itmes可以具有status ='paid'。即使订单中的其他商品的状态与“已付款”不同,也必须从我的查询结果中排除这些订单。

我如何解决这个问题:

paid_items = Items.where(status: 'paid').pluck(:order_id)
orders_wo_paid = Order.where('id NOT IN (?)', paid_items)

是否有ActiveRecord解决方案,可以在一个查询中解决此问题。 或者还有其他方法可以解决这个问题吗?

我不是在寻找红宝石解决方案,例如:

Order.select do |order|
  !order.items.pluck(:status).include?('paid')
        
end
感谢想法和灵感。

2 个答案:

答案 0 :(得分:1)

你可以这样做:

Order.where('orders.id NOT IN (?)', Item.where(status: 'paid').select(:order_id))

如果您正在使用Rails 4.x,那么:

Order.where.not(id: Item.where(status: 'paid').select(:order_id))

答案 1 :(得分:1)

您感兴趣的查询如下,但使用activerecord创建将很难/不可读:

SELECT 
  orders.*
FROM 
  orders
LEFT JOIN 
  order_items ON orders.id = order_items.order_id
GROUP BY
  order_items.order_id
HAVING 
  COUNT(DISTINCT order_items.id) = COUNT(DISTINCT order_items.status <> 'paid')

抱歉sql缩进,我不知道它的约定是什么。

使用rails(不幸的是为最重要的部分编写sql)的方法(根本不是最好的)将是以下内容:

Order.group(:order_id).joins("LEFT JOIN order_items ON orders.id = order_items.order_id")
     .having("COUNT(DISTINCT order_items.id) = COUNT(DISTINCT order_items.status = 'paid')")

当然,您可以使用AREL来摆脱硬编码的sql,但在我看来,阅读起来并不容易。

您可以在此要点中创建一个lefts连接示例:https://gist.github.com/mildmojo/3724189