基于ActiveRecord的多分组多排序复杂Rails查询需求
问题描述
数据表结构
Poi.rb
belongs_to :poitable, polymorphic: true, optional: true, dependent: :destroy, touch: true belongs_to :sleep, -> { where(pois: { poitable_type: 'Sleep' }) }, foreign_key: 'poitable_id' belongs_to :town, -> { where(pois: { poitable_type: 'Town' }) }, foreign_key: 'poitable_id'
默认作用域:default_scope { includes(:type_poi) }
Sleep & Town 模型
has_one :poi, as: :poitable, dependent: :destroy belongs_to :type_poi, inverse_of: :sleeps(或 :town) has_many :poi_traces, dependent: :destroy has_many :traces, through: :poi_traces, class_name: 'Trace'
PoiTrace.rb
class PoiTrace < ApplicationRecord belongs_to :poi belongs_to :trace end
当前查询语句
Poi.left_joins(:town, :sleep) .joins(:poi_traces) .where(poi_traces: { poi_traces: { trace_id: [1, 2] }, length_to_paris: 1_370_000..1_520_000}) .where(statut: [1, 2], poitable_type: ["Sleep", "Town"]) .uniq
查询需求
- 按
poi_traces.length_to_paris排序 - 按
pois.master_poi_id分组 - 按
pois.poitable_type排序("Town" 类型优先) - 在每个分组内按
poi.poitable.type_poi分组
期望结果格式
| poi_trace.length_to_paris | master_poi_id | poitable_type | poi.poitable.type_poi |
|---|---|---|---|
| 1520 | 2 | "Town" | "Town" |
| 2 | "Sleep" | "Camping" | |
| 2 | "Sleep" | "Camping" | |
| 2 | "Sleep" | "Hotel" | |
| 2 | "Sleep" | "Hotel" | |
| 2 | "Sleep" | "Lodge" | |
| 1510 | 1 | "Town" | "Town" |
| 1 | "Sleep" | "Camping" | |
| 1 | "Sleep" | "Camping" | |
| 1 | "Sleep" | "Hotel" | |
| 1 | "Sleep" | "Hotel" | |
| 1 | "Sleep" | "Lodge" | |
| 1490 | 4 | "Town" | "Town" |
| 4 | "Sleep" | "Camping" | |
| 4 | "Sleep" | "Camping" |
解决方案
修正后的查询语句
Poi.left_joins(:town, :sleep, poitable: :type_poi) .joins(:poi_traces) .where( poi_traces: { trace_id: [1, 2], length_to_paris: 1_370_000..1_520_000 }, statut: [1, 2], poitable_type: ["Sleep", "Town"] ) .order( 'poi_traces.length_to_paris DESC', 'pois.master_poi_id', "CASE pois.poitable_type WHEN 'Town' THEN 0 ELSE 1 END", 'type_pois.name' # 此处根据type_poi表的实际名称字段调整,比如type字段 ) .group_by(&:master_poi_id)
关键说明
- 修正查询错误:原语句中
poi_traces嵌套层级错误,已调整为正确的关联条件 - 排序逻辑:
- 先按
poi_traces.length_to_paris降序排列,匹配期望结果的顺序 - 按
master_poi_id分组排序 - 用CASE表达式让"Town"类型优先于"Sleep"
- 最后按关联的
type_poi字段排序
- 先按
- 分组处理:使用Ruby的
group_by(&:master_poi_id)在内存中分组,保留分组内所有明细记录,符合展示需求 - 性能优化:通过
left_joins(poitable: :type_poi)预加载关联数据,避免N+1查询
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

