我有一个具有关联购物车的用户模型。每个购物车都有一个 purchase_at 日期时间列。我想选择过去 3 个月内没有购买购物车的所有用户。
我虽然很简单:
User.joins(:carts).where('not carts.purchased_at < ?', 3.months.ago)
会做的伎俩,但情况似乎并非如此。我被退回过去 3 个月内购买过东西的用户记录。
有什么想法吗?
我有一个具有关联购物车的用户模型。每个购物车都有一个 purchase_at 日期时间列。我想选择过去 3 个月内没有购买购物车的所有用户。
我虽然很简单:
User.joins(:carts).where('not carts.purchased_at < ?', 3.months.ago)
会做的伎俩,但情况似乎并非如此。我被退回过去 3 个月内购买过东西的用户记录。
有什么想法吗?
您应该能够在普通的活动记录中做到这一点:
User.joins(:carts)
.group("users.id")
.having("MAX(carts.purchased_at) < ?", 3.months.ago)
我建议您执行原始 SQL。
User.find_by_sql('
SELECT users.* FROM users WHERE id NOT IN
(SELECT users.id FROM users LEFT JOIN carts ON users.id = carts.user_id
WHERE carts.purchased_at < ?)
', 3.months.ago)
请记住,这只是一个建议。(而且我认为代码需要一些重构,但你明白了。)
将squeel gem 用于此类复杂的查询:
User.where{id.not_in User.joins{carts}.where{carts.purchased_at > 3.months.ago}}
如果您使用的是 AR3(和 ARel):
User.joins(:carts).where(Cart.arel_table[:purchased_at].gt(3.months.ago))
如果您的购物车很大,使用MAX
and可能会很慢。GROUP BY
我会使用这种方法。
nq = Cart.where("carts.user_id = users.id AND carts.purchased_at >= ?", 3.months.ago)
User.where("NOT EXISTS (#{nq.to_sql})")
更好的是,我会添加一个名为模型的列last_purchase_at
,User
以使此查询高效。
class User
# add a new column called last_purchase_at
# index the last_purchase_at column
def self.dormant_users(period=3.months)
User.where("last_purchase_at <= ?", period.ago)
end
end
向模型添加 after_create 回调以Cart
更新User
模型。
class Cart
after_create :update_user_last_purchase_at
def update_user_last_purchase_at
user.update_attribute(:last_purchase_at, purchased_at)
end
end
将此代码添加到您的迁移脚本以设置last_purchase_at
现有User
模型的列。
User.connection.execute("
UPDATE users
JOIN (
SELECT a.user_id, MAX(a.purchased_at) purchased_at
FROM carts a
GROUP BY a.user_id
) carts ON carts.user_id= users.id
SET users.last_purchase_at = cards.purchased_at")
现在您可以按如下方式获取休眠用户:
User.dormant_users # dormant for last 6 months
User.dormant_users(6.months) # dormant for last 6 months