11

我有一个范围来限制所有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 范围不正确。我基本上想找到没有投票的地方。相反,它会尝试查找是否存在不等于当前用户的投票。任何想法如何返回不存在连接的所有记录?

4

3 回答 3

21

使用EXISTS表达式:

WHERE NOT EXISTS (
   SELECT FROM votes v  -- SELECT list can be empty
   WHERE  v.some_id = base_table.some_id
   AND    v.user_id = ?
   )

区别

... 在NOT EXISTS()(Ⓔ) 和NOT IN()(Ⓘ) 之间是双重的:

  1. 表现

    Ⓔ 一般比较快。一旦找到第一个匹配项,它就会停止处理子查询。手册:

    子查询通常只会执行足够长的时间来确定是否至少返回了一行,而不是一直到完成。

    Ⓘ 也可以通过查询计划器进行优化,但程度较小,因为NULL处理使其更复杂。

  2. 正确性

    如果子查询表达式中的结果值之一是NULL,Ⓘ的结果是NULL,而普通逻辑会期望TRUE- 并且Ⓔ将返回TRUE手册:

    如果所有每行结果不相等或为 null,且至少有一个为 null,则 的结果NOT IN为 null。

本质上,(NOT) EXISTS在大多数情况下是更好的选择。

例子

您的查询可能如下所示:

SELECT *
FROM   questions q
WHERE  NOT EXISTS (
    SELECT FROM votes v 
    WHERE  v.question_id = q.id
    AND    v.user_id = ?
    );

不要加入到基本查询中votes那将使努力无效。

除了NOT EXISTS和之外,NOT IN还有其他语法选项LEFT JOIN / IS NULLEXCEPT。看:

于 2013-01-10T14:46:37.447 回答
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) }
于 2013-01-10T05:18:16.733 回答
1

如果你想以EXISTS优雅和 Rails 风格的方式进行查询,你可以使用我写的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) }
于 2015-09-03T14:40:56.167 回答