我正在开发一个允许会员进行调查的应用程序(会员与Response有一对多的关系)。 Response保存member_id,question_id及其答案。
调查全部或全部提交,因此,如果该成员的回复表中有任何记录,他们已完成调查。
我的问题是,如何重新编写下面的查询,以便它真正起作用?在SQL中,这将是EXISTS关键字的主要候选者。
def surveys_completed
members.where(responses: !nil ).count
end
答案 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)
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
谢谢。
来源:
注意事项: