查找不存在连接的记录

时间:2013-01-10 05:09:43

标签: sql ruby-on-rails postgresql

我可以根据用户是否对其进行投票来限制所有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范围不正确。我基本上想找到没有投票的地方。相反,它试图寻找是否存在与当前用户不相等的投票。任何想法如何返回不存在连接的所有记录?

3 个答案:

答案 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)将返回 - TRUEThe 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 EXISTSNOT 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) }