Rails,ActiveRecord,嵌套模型上具有多个连接的复杂查询

时间:2013-04-28 21:39:15

标签: sql ruby-on-rails postgresql activerecord

我一直试图创建这个复杂的查询一段时间,但无法通过ActiveRecord获得。我正在尝试为指定的用户进行具有最高workout_set.weight的锻炼并锻炼并按重量进行排序。

模型看起来像这样(删除了错误的字段):

Workout
  belongs_to :user
  has_many :workout_exercises

WorkoutExercises
  belongs_to :workout
  belongs_to :exercise
  has_many :workout_sets

WorkoutSet
  belongs_to :workout_exercise
  weight

例如,使用以下数据(假设,exercise_id是相同的):

Steve:
  Workout 1:
     weight: 500
  Workout 2:
     weight: 400

Mark:
  Workout 1:
     weight: 300
  Workout 2:
     weight: 350

预期的结果集是:

  Steve's Workout 1
  Mark's Workout 2

这是在PostgreSql上,所以约束比sqLite和MySql更严格。

更新 由于我在PostgreSql上运行,因此DB对查询的order_by部分要严格得多。这是RSpec测试,为清晰起见写出了所有内容:

    it 'fetches the workout with the highest weight' do
            workout = create(:workout_with_exercises, user: user)
            workout2 = create(:workout_with_exercises, user: user)

            workout.workout_exercises[0].workout_sets[0].weight = 200
            workout.save
            workout2.workout_exercises[0].workout_sets[0].weight = 100
            workout2.save

            expect(user.workouts.count).to eq 2
            exercise = workout.workout_exercises[0]
            max_workout = Workout.joins(workout_exercises: :workout_sets)
                          .where('workout_exercises.exercise_id = ?', exercise.id)
                          .order('workouts.id, workout_sets.weight DESC')
                          .select("workouts.id, workout_sets.weight")
                          .uniq
            #max_workout = user.workouts.max_weight(workout.workout_exercises[0])

            expect(max_workout).to eq [workout]
          end

实际上会引发#异常。我已经尝试过这个查询的一堆东西,但仍然无法让它工作。我最终尝试使用以下查询在直接SQl中执行此操作(不包括user.id子句),但我得到一个空结果集:

max_workout = Workout.find_by_sql("
      SELECT workouts.* 
      FROM workouts,  
        (SELECT DISTINCT workouts.id AS workout_id, workout_sets.weight AS weight
         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 
         WHERE workout_exercises.exercise_id = #{exercise.id}
         ORDER BY workouts.id, workout_sets.weight DESC) AS myquery
      WHERE workouts.id = myquery.workout_id")

2 个答案:

答案 0 :(得分:1)

给定一个练习实例exercise,您可以选择不同的workouts,嵌套加入:workout_exercises:workout_sets,过滤exercise_id,然后按{排序{1}}如下:

workout_sets.weight

答案 1 :(得分:1)

经过大量的工作和更多的研究,这是查询产生了所需的结果集:

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
    ),

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 (SELECT p.user_id, MAX(weight) as weight
        FROM joined_table p
        GROUP BY p.user_id) 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 
    AND result_set.exercise_id = 1 -- arbitrary exercise ID
    AND workouts.user_id IN (1,2) -- arbitrary set of user IDs