9

次のSQLクエリをActiveRecordリレーションに変換してスコープで展開するにはどうすればよいですか?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で試みなければならないものですか?

私はそれをスコープ/サブクエリに分解しようとしましたが、サブクエリの選択が囲むクエリで終わるので、列が囲み内のGROUP BYまたはORDER BYで指定されていないためPostgreSqlエラーが発生しますステートメント。

更新: あなたはPostgreSQLだと仮定しても問題ありません。私はあなたのクエリを試みましたが、それはストレートクエリとActiveRecord等価のためにPG::Error: ERROR: column "rownum" does not existを投げます。

ただし、別のクエリでクエリをラップすると機能します。私は、選択がデータセットに投影されるまで、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 = <<-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) as row_num 
    SELECT 

    Workout.joins(", (#{Workout.joins(:workout_exercises => :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") 

をしかし、あなたが言うことができるように、それは非常にハックだと大規模なコードのにおいであるので、次のクエリは動作します。どのようにそれをリファクタリングするかについての任意のアイデア?

+0

このクエリを使用している直接ではありませんCTEさんActiveRecordまたはArelによってサポートされています。これをリファクタリングしてCTEを回避すると、どちらか一方でこれを行うことができます。 – PinnyM

+0

PostgreSQLを使用しているかどうか確認できますか? – PinnyM

+0

私もこの機能が必要です! –

答えて

6

あなたは明らかに、各ユーザーの最高体重と一致する最新のトレーニング(最高のID)の詳細を取得しようとしています。また、PostgreSQLを使用しているように見えます(MySQLにはCTEがありません)。

もしそうなら、あなたはウィンドウ関数を利用するとするクエリを簡素化することができます:ActiveRecordの中でのように記述することができる

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 

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") 
+0

Pinny、私はより多くの情報で元の質問を更新しました。助けてくれてありがとう。 – MunkiPhD

+0

@MunkiPhD:サブクエリが必要であることは間違いありません。Arelの 'from'と' sql'を使って更新しました。 – PinnyM

関連する問題