関連付けられたカートを持つユーザー モデルがあります。各カートには、 purchase_at datetime 列があります。過去 3 か月間カートを購入していないすべてのユーザーを選択したいと考えています。
私は簡単ですが:
User.joins(:carts).where('not carts.purchased_at < ?', 3.months.ago)
トリックを行いますが、そうではないようです。過去 3 か月間に何かを購入したユーザー レコードが返されます。
何かご意見は?
関連付けられたカートを持つユーザー モデルがあります。各カートには、 purchase_at datetime 列があります。過去 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}}
巨大なカートを持っている場合、使用するMAXと遅くなる可能性があります。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
AR3 (および ARel) を使用している場合:
User.joins(:carts).where(Cart.arel_table[:purchased_at].gt(3.months.ago))