我有两个模型,Project 和 Category,它们之间存在多对多关系。项目模型非常简单:
class Project < ActiveRecord::Base
has_and_belongs_to_many :categories
scope :in_categories, lambda { |categories|
joins(:categories).
where("categories.id in (?)", categories.collect(&:to_i))
}
end
:in_categories 范围采用一组类别 ID(作为字符串),因此使用此范围我可以取回属于至少一个传入类别的每个项目。
但我实际上想做的是过滤(更好的名称是:has_categories)。我只想获得属于所有传入类别的项目。所以如果我传入 ["1", "3", "4"] 我只想获得属于所有类别的项目。
最佳答案
SQL 中有两种常见的解决方案来执行您所描述的操作。
自加入:
SELECT ...
FROM Projects p
JOIN Categories c1 ON c1.project_id = p.id
JOIN Categories c3 ON c3.project_id = p.id
JOIN Categories c4 ON c4.project_id = p.id
WHERE (c1.id, c3.id, c4.id) = (1, 3, 4);
注意我使用语法来比较元组。这相当于:
WHERE c1.id = 1 AND c3.id = 3 AND c4.id = 4;
一般来说,如果您有覆盖索引,自连接解决方案具有非常好的性能。大概
Categories.(project_id,id)
将是正确的索引,但可以使用 EXPLAIN 分析 SQL。这种方法的缺点是,如果您要搜索与四个不同类别匹配的项目,则需要四个联接。五个类别的五个连接等。
通过...分组:
SELECT ...
FROM Projects p
JOIN Categories cc ON c.project_id = p.id
WHERE c.id IN (1, 3, 4)
GROUP BY p.id
HAVING COUNT(*) = 3;
如果您使用的是 MySQL(我假设您是),大多数 GROUP BY 查询会调用临时表,这会降低性能。
我将把它留作练习,让您将这些 SQL 解决方案之一调整为等效的 Rails ActiveRecord API。
关于sql - 如何编写命名范围以通过传入的所有数组进行过滤,而不仅仅是通过匹配一个元素(使用 IN),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3243913/