ActiveRecord查询以查找在过去3个月内未进行购买的所有用户

时间:2012-11-22 16:44:20

标签: ruby-on-rails ruby-on-rails-3 activerecord arel

我有一个具有关联购物车的用户模型。每个购物车都有一个purchase_at日期时间列。 我想选择过去3个月内没有购买过购物车的所有用户。

我虽然很简单:

User.joins(:carts).where('not carts.purchased_at < ?', 3.months.ago)

会做的伎俩,但似乎并非如此。我正在退回过去3个月内购买过的东西的用户记录。

有什么想法吗?

5 个答案:

答案 0 :(得分:4)

你应该可以做到这一点是在普通的活跃记录中:

User.joins(:carts)
  .group("users.id")
  .having("MAX(carts.purchased_at) < ?", 3.months.ago)

答案 1 :(得分:1)

我建议你做一个原始的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)

请记住,这只是一个建议。 (我认为代码需要一些重构,但你明白了。)

答案 2 :(得分:1)

使用squeel gem进行此类复杂查询:

User.where{id.not_in User.joins{carts}.where{carts.purchased_at > 3.months.ago}}

答案 3 :(得分:0)

如果你正在使用AR3(和ARel):

User.joins(:carts).where(Cart.arel_table[:purchased_at].gt(3.months.ago))

答案 4 :(得分:0)

如果你有一个巨大的购物车,使用MAXGROUP 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