Ruby on Rails如何将可用客房查询改写为带左连接的lambda作用域
问题出在你把日期过滤条件写在了全局WHERE子句中:左连接后没有对应日期预订的客房,关联出来的bookings相关字段都是NULL,WHERE bookings.day = ? 会直接把这些NULL值的行过滤掉,导致完全没有预订记录的空房被排除。
你需要把日期过滤条件移到左连接的关联条件中,这样左连接仅会匹配指定日期的预订记录,没有匹配预订的客房仍然会保留在结果集中,COUNT(bookings.id)统计值为0,自然满足小于3的筛选要求,不需要额外判断空预订的情况。
正确的scope实现
scope :available, lambda { |date| left_joins("LEFT OUTER JOIN bookings ON bookings.room_id = rooms.id AND bookings.day = ?", date) .group("rooms.id") .having("COUNT(bookings.id) < 3") }
如果你偏好使用Arel避免硬写SQL,可以用更安全的版本:
scope :available, lambda { |date| rooms = Room.arel_table bookings = Booking.arel_table join_on = rooms[:id].eq(bookings[:room_id]).and(bookings[:day].eq(date)) joins(rooms.join(bookings, Arel::Nodes::OuterJoin).on(join_on).join_sources) .group(rooms[:id]) .having(bookings[:id].count.lt(3)) }
两种写法执行逻辑完全一致:
- 左连接时就限定只关联对应日期的预订记录
- 按客房ID分组后统计每个客房的匹配预订数
- 筛选出预订数小于3的客房,其中预订数为0的空房自然会被包含在结果中
内容的提问来源于stack exchange,提问作者Irina
相关产品推荐
相关产品推荐

