我有 2 个模型:
DeliverySlot has_many :orders Order belongs_to :delivery_slot
Delivery Slots 对它们可以容纳的订单数量有限制。我想创建一个范围来提供所有可用的交付槽。可用的交货时段是尚未达到其关联订单限制的交货时段。
我的尝试看起来像:
scope :available, where("limit > ?", order.count).joins(:orders)
order.count 是上面的伪代码。
我有 2 个模型:
DeliverySlot has_many :orders Order belongs_to :delivery_slot
Delivery Slots 对它们可以容纳的订单数量有限制。我想创建一个范围来提供所有可用的交付槽。可用的交货时段是尚未达到其关联订单限制的交货时段。
我的尝试看起来像:
scope :available, where("limit > ?", order.count).joins(:orders)
order.count 是上面的伪代码。
要做到这一点,就像你有设置一样,你需要使用orders.count而不是order.count因为你指的是关联。这将提示 ActiveRecord 组装一个看起来像SELECT COUNT(*) FROM orders WHERE delivery_slot_id = 1.
Rails 实际上足够聪明,可以在您where适当地传递它时将其用作条件中的子查询,例如 la where('limit > ', orders.count)。但正如您可能看到的,如果它是预编译的,这将不起作用,因为查询在条件中使用了显式 ID。
相反,您需要计算条件不明确的订单,然后将其用作子查询:where('limit > ?', Order.where(delivery_slot_id: 'delivery_slots.id').count)。如果您尝试单独运行订单计数的查询,它将失败delivery_slots,但因为它在此处的子查询中,您应该一帆风顺。
我想提出另一种完全这样做的方法,使用计数器缓存:
class AddCounterCacheToDeliverySlots < ActiveRecord::Migration
class DeliverySlot < ActiveRecord::Base; end
def change
add_column :delivery_slots, :orders_count, :integer, default: 0
add_index :delivery_slots, [:orders_count, :limit]
DeliverySlot.reset_column_information
DeliverySlot.find_each do |delivery_slot|
DeliverySlot.update_counters delivery_slot.id, orders_count: delivery_slot.orders.count
end
end
end
class Order < ActiveRecord::Base
belongs_to :delivery_slot, counter_cache: true
end
class DeliverySlot < ActiveRecord::Base
has_many orders
scope :available, where('orders_count < limit')
end
Rails 会自动递增和递减orders_counteach 的列DeliverySlot,并且因为它是索引的,所以查询起来快得离谱。
所以我找到了一种在 SQL 中执行此操作的方法。如果有人知道一种更 ruby 的方式而不创建大量数据库查询,请加入。
scope :available, joins('LEFT JOIN orders
ON orders.delivery_slot_id = delivery_slots.id')
.where("delivery_slots.limit > (
SELECT COUNT(*) FROM orders
WHERE orders.delivery_slot_id = delivery_slots.id )
")
scope :available, lambda {
|delivery_slot| joins(:orders).
where("limit > ?", order.count)
}
尝试这个