将SQL查询转换为ActiveRecord Relation

如何将以下SQL查询转换为ActiveRecord关系,以便我可以使用范围扩展它?

WITH joined_table AS ( SELECT workout_sets.weight AS weight, workouts.user_id AS user_id, workouts.id AS workout_id, workout_sets.id AS workout_set_id, workout_exercises.exercise_id AS exercise_id FROM workouts INNER JOIN workout_exercises ON workout_exercises.workout_id = workouts.id INNER JOIN workout_sets ON workout_sets.workout_exercise_id = workout_exercises.id ORDER BY workout_sets.weight DESC ), sub_query AS ( SELECT p.user_id, MAX(weight) as weight FROM joined_table p GROUP BY p.user_id ), result_set AS ( SELECT MAX(x.workout_id) AS workout_id, x.user_id, x.weight, x.workout_set_id, x.exercise_id FROM joined_table x JOIN sub_query y ON y.user_id = x.user_id AND y.weight = x.weight GROUP BY x.user_id, x.weight, x.workout_set_id, x.exercise_id ORDER BY x.weight DESC) SELECT workouts.*, result_set.weight, result_set.workout_set_id, result_set.exercise_id FROM workouts, result_set WHERE workouts.id = result_set.workout_id 

这是我必须尝试直接ARel的事情吗?

我已经尝试将其分解为范围/子查询,但子查询上的选择最终在封闭查询中,因此抛出PostgreSql错误,因为未在封闭语句中的GROUP BY或ORDER BY中指定该列。

更新:您认为它是PostgreSql是正确的。 我尝试了你的查询,但是对于直接查询和ActiveRecord等价,它抛出了PG::Error: ERROR: column "rownum" does not exist

但是,当我将查询包装在单独的查询中时,它可以工作。 我假设在将select投影到数据集之后才会创建ROW_NUMBER()。 所以以下查询有效:

 SELECT workouts.*, t.weight, t.workout_set_id, t.exercise_id, t.row_num FROM workouts, (SELECT workouts.id as workout_id, workout_sets.weight as weight, workout_sets.id AS workout_set_id, workout_exercises.id AS exercise_id, ROW_NUMBER() OVER ( PARTITION BY workouts.user_id ORDER BY workout_sets.weight DESC, workouts.id DESC ) row_num FROM workouts JOIN workout_exercises ON workout_exercises.workout_id = workouts.id JOIN workout_sets ON workout_sets.workout_exercise_id = workout_exercises.id) as t WHERE workouts.id = t.workout_id AND t.row_num = 1 

我已成功按摩以下内容:

  selected_fields = < :workout_sets).select(selected_fields).to_sql}) as t").select("workouts.*, t.*").where("workouts.id = t.workout_id AND t.row_num = 1").order("t.weight DESC") 

但正如你所知,这是非常hacky并且是一个巨大的代码味道。 有关如何重构的任何想法?

您显然正在尝试获得与每个用户的最高权重相匹配的最新锻炼(最高ID)细节。 你似乎也在使用PostgreSQL(MySQL没有CTE),如果我错了,请纠正我。

如果是这样,您可以使用窗口函数并将查询简化为:

 SELECT * FROM ( SELECT workouts.*, workout_sets.weight, workout_sets.id AS workout_set_id, workout_exercises.id AS exercise_id, ROW_NUMBER() OVER ( PARTITION BY workouts.user_id ORDER BY workout_sets.weight DESC, workouts.id DESC ) as rowNum FROM workouts JOIN workout_exercises ON workout_exercises.workout_id = workouts.id JOIN workout_sets ON workout_sets.workout_exercise_id = workout_exercises.id ) t WHERE rowNum = 1 

在ActiveRecord中可以写为:

 selected_fields = <<-SELECT workouts.*, workout_sets.weight, workout_sets.id AS workout_set_id, workout_exercises.id AS exercise_id, ROW_NUMBER() OVER ( PARTITION BY workouts.user_id ORDER BY workout_sets.weight DESC, workouts.id DESC) as rowNum SELECT subquery = Workout.joins(:workout_exercises => :workout_sets). select(selected_fields).to_sql Workout.select("*").from(Arel.sql("(#{subquery}) as t")) .where("rowNum = 1")