我正在开发一个允许会员进行调查的应用程序(会员与响应具有一对多的关系)。Response 包含 member_id、question_id 及其答案。
调查要么全部提交,要么不提交,因此如果该成员的响应表中有任何记录,则他们已完成调查。
我的问题是,如何重新编写下面的查询以使其真正起作用?在 SQL 中,这将是 EXISTS 关键字的主要候选对象。
def surveys_completed
members.where(responses: !nil ).count
end
我正在开发一个允许会员进行调查的应用程序(会员与响应具有一对多的关系)。Response 包含 member_id、question_id 及其答案。
调查要么全部提交,要么不提交,因此如果该成员的响应表中有任何记录,则他们已完成调查。
我的问题是,如何重新编写下面的查询以使其真正起作用?在 SQL 中,这将是 EXISTS 关键字的主要候选对象。
def surveys_completed
members.where(responses: !nil ).count
end
您可以使用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 完全按照这里的要求:使用EXISTS
to 来做一个条件。它适用于最新的 Rails 4.1。
members.where_assoc_exists(:responses)
它还可以做得更多!
类似的问题:
您可以使用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
您还可以使用子查询:
members.where(id: Response.select(:member_id))
与之相比,includes
它不会加载关联的模型(如果您不需要它们,这是一个性能优势)。
如果您使用 Rails 5 及更高版本,则应使用left_joins
. 否则,手动“左外连接”也将起作用。这比https://stackoverflow.com/a/18234998/3788753includes
中提到的使用性能更高。将尝试将相关对象加载到内存中,而将构建“LEFT OUTER JOINS”查询。includes
left_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
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
谢谢。
资料来源:
笔记: