如何调整ActiveRecord(或直接SQL)查询以包含带有WHERE子句的JOIN上的零计数记录

我有一个关于在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次(包括零次)?

问题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 

如果鞋没有购买,则左连接意味着对于该鞋,结果集对于购买表中的所有列将具有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对该鞋子的购买