ActiveRecord:如何查找不含指定子记录的Material父模型?
筛选不包含特定子记录的Material模型
我明白你的需求了——你要找的是完全没有关联name为"abc"的AttributeType子记录的Material,而不是只是排除掉子记录里的"abc"项。之前的写法之所以不对,是因为只要某个Material还有其他非"abc"的AttributeType,它就会被误选进来。
这里有几种高效的解决方式,优先推荐数据库层面的查询(比在内存里过滤性能好得多):
方法一:子查询排除法(推荐)
直接用Active Record的子查询,让数据库帮你过滤掉所有关联过"abc"的Material:
# 获取@place下所有没有name为"abc"的AttributeType的Material materials = @place.materials.where.not( id: AttributeType.where(name: "abc").select(:material_id) )
原理很简单:先找出所有关联了"abc"的AttributeType对应的material_id,然后排除这些ID的Material,剩下的就是完全没有该子记录的目标数据。
方法二:左连接+条件筛选
通过左连接关联AttributeType,然后筛选出没有匹配到"abc"的记录:
materials = @place.materials.left_joins(:attribute_types) .where("attribute_types.name != ? OR attribute_types.id IS NULL", "abc") .group("materials.id") .having("COUNT(attribute_types.id) = SUM(CASE WHEN attribute_types.name != 'abc' THEN 1 ELSE 0 END)")
这个写法确保了Material的所有AttributeType都不是"abc",包括那些没有任何AttributeType的Material(attribute_types.id IS NULL的情况)。
方法三:内存层面过滤(小数据量适用)
如果你的数据集很小,也可以在加载后用Ruby的枚举方法筛选:
materials = @place.materials.select do |material| material.attribute_types.none? { |at| at.name == "abc" } end
注意:这个方法会加载@place下所有的Material及其AttributeType到内存,数据量大时不建议使用。
对比你之前的写法,核心区别在于:我们不是过滤子记录,而是直接排除所有存在"abc"子记录的父模型,这样就不会得到不需要的Material了。
内容的提问来源于stack exchange,提问作者IBG
相关产品推荐
相关产品推荐

