Rails技术问题:如何筛选未关联ID为1的标签的书籍
搞定!筛选未关联指定标签的书籍
这个问题我之前做Rails项目时也踩过坑,先给你捋清楚为啥原来的查询会出错,再给你两种靠谱的解决思路:
为啥你的原查询不对?
你写的Book.includes(:tags).where.not(tags: { id: 1 }),背后生成的SQL会把书籍和标签做左连接,然后筛选标签ID不等于1或者标签为空的记录。但如果一本书同时关联了标签1和其他标签(比如标签2),关联表里会有两条记录:一条是标签1,一条是标签2。这时候WHERE条件会匹配到标签2的那条记录,导致这本书被错误地留在结果里——这就是为啥你会看到包含标签1的书也被返回了。
方法一:用分组+Having精准筛选
这种方法通过左连接关联表,然后按书籍分组,统计每个书籍关联标签1的次数,只要次数为0,就是完全没关联标签1的书(包括没有任何标签的书):
Book.left_joins(:tags) .group('books.id') .having('COUNT(CASE WHEN tags.id = 1 THEN 1 END) = 0')
逻辑非常直白:对每本书,只统计标签ID为1的数量,数量是0就说明这本书要么没标签,要么标签里绝对没有ID为1的。
方法二:用子查询排除已关联的书籍
先找出所有关联了标签1的书籍ID,然后直接排除这些ID,剩下的就是符合要求的书:
Book.where.not(id: Book.joins(:tags).where(tags: { id: 1 }).select(:id))
或者如果你已经拿到了标签1的对象,也可以简化成:
Book.where.not(id: Tag.find(1).books.select(:id))
这种方式逻辑清晰,性能也不错,因为子查询只会返回需要排除的书籍ID集合,不会加载多余的数据。
内容的提问来源于stack exchange,提问作者Railsana
相关产品推荐
相关产品推荐

