Rails活动记录查询与'exists'的关联

时间:2013-08-14 14:33:27

标签: ruby-on-rails

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

调查全部或全部提交,因此,如果该成员的回复表中有任何记录,他们已完成调查。

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

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

5 个答案:

答案 0 :(得分:97)

您可以使用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

类似问题:

答案 1 :(得分:7)

您可以使用Where Exists gem以优雅的Rails-ish方式使用SQL EXISTS关键字:

members.where_exists(:responses).count

当然你也可以使用原始SQL:

members.where("EXISTS" \
  "(SELECT 1 FROM responses WHERE responses.member_id = members.id)").
  count

答案 2 :(得分:2)

您还可以使用子查询:

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

与具有includes的内容相比,它不会加载关联的模型(如果您不需要它们,这会带来性能优势)。

答案 3 :(得分:2)

如果您使用的是Rails 5和更高版本,则应使用left_joins。否则,也可以使用手动“左外连接”。与使用https://stackoverflow.com/a/18234998/3788753中提到的includes相比,它的性能更高。 includes将尝试将相关对象加载到内存中,而left_joins将构建“ LEFT OUTER 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

答案 4 :(得分:0)

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 

谢谢。

来源:

注意事项: