54

我正在开发一个允许会员进行调查的应用程序(会员与响应具有一对多的关系)。Response 包含 member_id、question_id 及其答案。

调查要么全部提交,要么不提交,因此如果该成员的响应表中有任何记录,则他们已完成调查。

我的问题是,如何重新编写下面的查询以使其真正起作用?在 SQL 中,这将是 EXISTS 关键字的主要候选对象。

 def surveys_completed
    members.where(responses: !nil ).count
 end 
4

5 回答 5

113

您可以使用includes然后测试相关响应是否存在,如下所示:

def surveys_completed
  members.includes(:responses).where('responses.id IS NOT NULL')
end

这是一个替代方案,带有joins

def surveys_completed
  members.joins(:responses)
end

使用 Rails 4 的解决方案

def surveys_completed
  members.includes(:responses).where.not(responses: { id: nil })
end

使用的替代解决方案activerecord_where_assoc:这个 gem 完全按照这里的要求:使用EXISTSto 来做一个条件。它适用于最新的 Rails 4.1。

members.where_assoc_exists(:responses)

它还可以做得更多!


类似的问题:

于 2013-08-14T14:50:01.450 回答
7

您可以使用Where Exists gemEXISTS以优雅的 Rails 方式使用 SQL关键字:

members.where_exists(:responses).count

当然,您也可以使用原始 SQL:

members.where("EXISTS" \
  "(SELECT 1 FROM responses WHERE responses.member_id = members.id)").
  count
于 2015-09-03T14:55:42.010 回答
6

您还可以使用子查询:

members.where(id: Response.select(:member_id))

与之相比,includes它不会加载关联的模型(如果您不需要它们,这是一个性能优势)。

于 2016-08-04T14:42:00.697 回答
6

如果您使用 Rails 5 及更高版本,则应使用left_joins. 否则,手动“左外连接”也将起作用。这比https://stackoverflow.com/a/18234998/3788753includes中提到的使用性能更高。将尝试将相关对象加载到内存中,而将构建“LEFT OUTER JOINS”查询。includesleft_joins

def surveys_completed
  members.left_joins(:responses).where.not(responses: { id: nil })
end

即使没有相关记录(如上面您通过 nil 查找的查询)includes仍然使用更多内存。在我的测试中,我发现在 Rails 5.2.1 上使用了大约 33 倍的内存。在 Rails 4.2.x 上,与手动进行连接相比,内存增加了约 44 倍。

请参阅此测试要点: https ://gist.github.com/johnathanludwig/96fc33fc135ee558e0f09fb23a8cf3f1

于 2018-10-29T22:14:41.827 回答
1

where.missing (Rails 6.1+)

Rails 6.1 引入了一种检查关联缺失的新方法 - where.missing

请看下面的代码片段:

# Before:
Post.left_joins(:author).where(authors: { id: nil })

# After:
Post.where.missing(:author)

这是一个在后台使用的 SQL 查询示例:

Post.where.missing(:author)
# SELECT "posts".* FROM "posts"
# LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# WHERE "authors"."id" IS NULL

因此,您的特定情况可以重写如下:

def surveys_completed
  members.where.missing(:response).count
end 

谢谢。

资料来源:

笔记:

于 2021-02-16T01:03:58.853 回答