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按预期工作,因为它将返回所有购买次数少于库存数量的鞋子(在本例中为 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:从长远来看,当有几十万/几百万只鞋子时,这个问题的最佳解决方案是什么?

提前致谢!

============================ 一个解决方案(仅适用于 MySQL 而不是 PostgresSQL)感谢@Frederick Cheung 指路

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

但是我认为这仍然不能满足您的要求:如果该客户和其他客户购买了鞋子,这只会使报告的计数为 1 而不是 2,而不是完全删除该行

您可以通过第二次加入购买表来做到这一点

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 回答