我可以根据用户是否对其进行投票来限制所有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范围不正确。我基本上想找到没有投票的地方。相反,它试图寻找是否存在与当前用户不相等的投票。任何想法如何返回不存在连接的所有记录?
答案 0 :(得分:15)
使用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)的结果可能是NULL
您期望的结果 - 并且(e)将返回 - TRUE
。 The manual:
如果所有每行结果不等或为null,至少为 一个为null,则
NOT IN
的结果为空。
基本上,(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 :(得分:1)
尝试这个,让我知道它是否有效
修改-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) }
答案 2 :(得分:1)
如果你想以优雅和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) }