0

ActiveRecordでクエリを作成することについて質問がありますが、ActiveRecordに精通していない人のためにSQLも提供しています。

私は次のモデルクラスを持っています:

class Shoe < ActiveRecord::Base
  has_many :purchases

  def self.available_shoes
    #show all shoes that have been purchased less than num_in_stock
    num_in_stock = 3
    Shoe.includes(:purchases)
        .group("purchases.shoe_id")
        .having("COUNT(purchases.shoe_id) < ?", num_in_stock)
  end

  def self.available_shoes_for_user(user)
    #show all available_shoes that a user hasn’t already purchased
    Shoe.available_shoes.where("purchases.user_id != ?", user.id)
  end
end

この方法Shoe.available_shoesは、0回購入された靴を含め、在庫のある量(この場合は3)よりも少ない回数購入されたすべての靴を返すという点で期待どおりに機能します。

電話をかけると問題が発生Shoe.available_shoes_for_user(user)します。既存の購入があるすべての靴が表示されますが、購入がゼロの利用可能な靴は表示されません。

以下の生のSQLを抽出しました:

#Shoe.available_shoes
SELECT shoes.*, purchases.* FROM shoes LEFT OUTER JOIN purchases ON purchases.shoe_id = shoes.id GROUP BY shoe_id HAVING COUNT(purchases.shoe_id) < 3

#Shoe.available_shoes_for_user(User.find(5))
SELECT shoes.*, purchases.* FROM shoes LEFT OUTER JOIN purchases ON purchases.shoe_id = shoes.id WHERE (purchases.user_id != 5) GROUP BY shoe_id HAVING COUNT(purchases.shoe_id) < 3

質問1:Shoe.available_shoes_for_user(user)クライアントがまだ購入していない3回未満(0回を含む)に購入したすべての靴を表示する(つまり、購入したすべての靴を表示する)ようにするにはどうすればよいですか?

質問2:長期的には、数十万/数百万の靴がある場合、この問題の最適な解決策は何でしょうか?

前もって感謝します!

============================ 解決策(PostgresSQLではなくMySQLでのみ機能します)道を示してくれた@FrederickCheungに感謝します

class Shoe < ActiveRecord::Base
  has_many :purchases

  def self.available_shoes
    #show all shoes that have been purchased less than num_in_stock
    num_in_stock = 3
    Shoe.includes(:purchases)
        .group("purchases.shoe_id")
        .having("COUNT(purchases.shoe_id) < ?", num_in_stock)
  end

  def self.available_shoes_for_user(user)
    #show all available_shoes that a user hasn’t already purchased
    Shoe.available_shoes
         .joins("LEFT OUTER JOIN purchases purchased_by_user ON purchased_by_user.shoe_id = shoes.id AND purchased_by_user.user_id = '#{user.id}'")
         .where("purchased_by_user.id IS NULL")
  end
end
4

1 に答える 1

1

靴の購入がない場合、左結合は、その靴の結果セットの購入テーブルのすべての列がNULLになることを意味します。

そのユーザーによる購入を削除する句を適用しwhere purchases.user_id != 5ていますが、それはNULL行も除外しています。その条件を次のように変更できます

where purchases.id is null or purchases.user_id != 5

しかし、これでもあなたが望むことはできないと思います。その顧客と他の顧客が靴を購入した場合、行を完全に削除するのではなく、報告されたカウントが2ではなく1になります。

これを行うには、購入テーブルにもう一度参加します

left outer join purchases on purchases.shoe_id = shoes.id
left outer join purchases purchased_by_user on purchased_by_user.shoe_id = shoes.id and purchased_by_user.user_id = 5

次に、where句はpurchased_by_user.id is null、データベースがそのuser_idでその靴の購入を見つけることができなかったことを意味することを確認する必要があります。

于 2012-07-10T08:01:00.250 に答える