Rails 6如何查询指定用户finished会议数小于2的School记录
可以通过单条查询实现
你原有查询失效的核心原因是joins(:meetings)默认生成内连接(INNER JOIN),只会返回至少存在1条关联meetings记录的学校,所有没有对应符合条件会议的学校(包含用户在学校下无finished状态会议、学校无任何会议的场景)都会被直接过滤,无法命中结果。
正确实现代码
核心思路是改用带匹配条件的左外连接(LEFT OUTER JOIN):把「指定用户、finished状态」的筛选条件放到连接的ON子句中,保证所有学校记录都会被保留,匹配不到符合条件会议的学校统计到的会议数为0,自然满足「数量小于2」的规则。
以下是兼容Rails 5及以上版本的稳妥写法,用Arel构造查询避免SQL注入和硬编码表名问题:
# target_user 为传入的指定User实例,例如 User.find_by(name: 'Adam') target_user = <你的目标用户对象> schools = School.arel_table meetings = Meeting.arel_table # 构造带筛选条件的左外连接 finished_meeting_join = meetings.create_join( meetings, meetings.create_on( meetings[:school_id].eq(schools[:id]) .and(meetings[:user_id].eq(target_user.id)) .and(meetings[:status].eq('finished')) ), Arel::Nodes::OuterJoin ) # 最终查询 School.joins(finished_meeting_join) .group(schools[:id]) .having(meetings[:id].count.lt(2))
如果你觉得Arel写法过于繁琐,也可以用参数绑定的方式手写左连接SQL,安全性同样有保障:
target_user = <你的目标用户对象> School.joins( ActiveRecord::Base.sanitize_sql([ "LEFT OUTER JOIN meetings ON meetings.school_id = schools.id AND meetings.user_id = ? AND meetings.status = 'finished'", target_user.id ]) ).group('schools.id').having('COUNT(meetings.id) < 2')
结果验证
用你给出的示例数据,传入用户Adam执行上述查询:
- 学校A:匹配到2条Adam的finished状态会议,count值为2,被having条件过滤
- 学校B:无Adam的finished状态会议,count值为0,满足条件返回
- 学校C:无Adam的finished状态会议(仅存在John的finished会议),count值为0,满足条件返回
- 学校D:无任何会议记录,count值为0,满足条件返回
- 学校E:无Adam的finished状态会议(仅存在John的finished会议),count值为0,满足条件返回
最终返回结果正好是预期的B、C、D、E四所学校。
避坑提醒
- 不要直接把
user: User类对象写入查询条件,需传入具体用户实例,通过user_id做匹配 - 不要把会议的用户、状态筛选条件全部放到全局
where子句中,否则左外连接会退化为内连接,重新出现无法匹配无会议学校的问题 - 手写SQL时不要直接拼接用户传入的参数,必须用框架自带的参数绑定方法做转义,避免SQL注入风险
内容的提问来源于stack exchange,提问作者N0ne
相关产品推荐
相关产品推荐

