You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Ruby on Rails中使用Joins结合左外连接查询?

解决方案:统计学校学生书籍总页数(含无书籍学生)

数据库关联结构

School
has_many :students

Student
has_many :books

Book
# 包含pages整数属性

需求与问题

需要统计学校层面所有学生书籍的总页数,必须包含无书籍的学生,且要通过Ruby代码实现等价于以下SQL的查询逻辑:

SELECT * FROM school INNER JOIN students ON students.school_id = schools.id LEFT OUTER JOIN books ON books.student_id = students.id

此前使用School.joins({:students => :books}).all无法达成需求,原因是joins默认生成内连接,会自动过滤掉没有关联书籍的学生记录。

实现方案

1. 生成目标等价SQL的查询

要实现目标中的内连接+左外连接逻辑,在Rails 5及以上版本可以用left_joins(别名left_outer_joins)来指定左外连接:

# 与目标SQL逻辑完全一致的查询
School.joins(:students).left_joins(students: :books).all
  • joins(:students):建立学校与学生的内连接,确保只返回有学生的学校(和目标SQL一致)
  • left_joins(students: :books):建立学生与书籍的左外连接,保留所有学生,包括无书籍的

2. 直接统计学校总页数

如果不需要加载所有记录,而是直接统计每个学校的学生书籍总页数,可以结合聚合函数和COALESCE处理无书籍的情况(将NULL转为0):

# 统计每个学校的学生书籍总页数
School.joins(:students)
      .left_joins(students: :books)
      .select('schools.id, schools.name, COALESCE(SUM(books.pages), 0) AS total_pages')
      .group('schools.id')

返回的结果中,total_pages字段就是对应学校所有学生书籍的总页数,无书籍的学生贡献0页。

关于includes的补充

如果只是需要预加载学校、学生及关联书籍(避免N+1查询),includes也能实现加载所有学生(含无书籍的),但它的核心是预加载而非统计:

# 预加载所有关联数据,包含无书籍的学生
School.includes(students: :books).all

这个方法会生成左连接SQL(或分两次查询),但如果是做统计场景,优先用left_joins结合聚合的方式更高效。

内容的提问来源于stack exchange,提问作者Sayan Patel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 20:44:57