查找不存在连接的记录
我可以根据用户是否对其进行投票来限制所有questions
。 在模型中:
scope :answered_by, lambda {|u| joins(:votes).where("votes.user_id = ?", u.id) } scope :unanswered_by, lambda {|u| joins(:votes).where("votes.user_id != ?", u.id) }
在控制器中,我这样称呼它们:
@answered = Question.answered_by(current_user) @unanswered = Question.unanswered_by(current_user)
unanswered_by范围不正确。 我基本上想找到没有投票的地方。 相反,它试图寻找是否存在与当前用户不相等的投票。 任何想法如何返回不存在连接的所有记录?
使用EXISTS
反半连接:
WHERE NOT EXISTS ( SELECT 1 FROM votes v WHERE v.some_id = base_table.some_id AND v.user_id = ? )
区别
…… NOT EXISTS()
(e)和NOT IN()
(i)之间是双重的:
-
性能
(e)通常更快。 一旦找到第一个匹配的行,它就可以停止扫描表或索引。 (i)也可以由查询规划器进行优化,但程度较小,因为NULL
处理更复杂。 -
正确性
如果子查询表达式中的结果值之一可以为NULL
,则(i)的结果可能是您期望的结果 – 并且(e)将返回 –TRUE
。 手册:
如果所有每行结果不等或为null,并且至少有一个null,则
NOT IN
的结果为null。
从本质上讲, (NOT) EXISTS
在大多数情况下是更好的选择。
例
您的查询可能如下所示:
SELECT * FROM questions q WHERE NOT EXISTS ( SELECT 1 FROM votes v WHERE v.question_id = q.id AND v.user_id = ? )
不要在基本查询中加入votes
。 这将使努力无效。
除了NOT EXISTS
和NOT IN
,还有LEFT JOIN / IS NULL
。 此相关答案中有更多语法变体:
- 选择其他表中不存在的行
试试这个,让我知道它是否有效
编辑-1
scope :unanswered_questions, lambda { joins('LEFT OUTER JOIN votes ON questions.id = votes.question_id').where('votes.question_id IS NULL') }
编辑-2
scope :unanswered_by, lambda {|u| where("questions.id NOT IN (SELECT votes.question_id from votes where votes.user_id = ?)",u.id) }
如果你想以优雅和Rails-ish的方式进行EXISTS
查询,你可以使用我写的Where Exists gem:
Question.where_not_exists(:votes, user_id: current_user.id)
当然,您也可以使用它的范围:
scope :unanswered_by, ->(user){ where_not_exists(:votes, user_id: user.id) }