9

Wie kann ich die folgende SQL-Abfrage in eine ActiveRecord-Beziehung umwandeln, sodass ich sie mit Bereichen erweitern kann?Verwandeln Sie die SQL-Abfrage in ActiveRecord Relation

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 

Ist das etwas, das ich mit geraden ARel versuchen müsste?

Ich habe versucht, es in Bereiche/Unterabfragen aufzuteilen, aber die Auswahlen auf die Unterabfragen enden in der umschließenden Abfrage und PostgreSql Fehler, da die Spalte nicht in den GROUP BYs oder ORDER BYs in der umschließenden angegeben ist Erklärung.

Aktualisierung: Sie sind in Ihrer Annahme richtig, dass es PostgreSql ist. Ich habe versucht, Ihre Abfrage, aber es wirft eine PG::Error: ERROR: column "rownum" does not exist, sowohl für die direkte Abfrage und die ActiveRecord Äquivalenz.

Wenn ich jedoch die Abfrage in einer separaten Abfrage umbrechen, funktioniert es. Ich gehe davon aus, dass die ROW_NUMBER() erst erstellt wird, nachdem die Auswahl auf den Datensatz projiziert wurde. So ist die folgende Abfrage funktioniert:

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 

Was ich habe es geschafft, in die folgende Massage:

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

Aber wie Sie sagen können, das ist extrem Hacky und ist ein massiver Code Geruch. Irgendeine Idee, wie man das umgestaltet?

+0

Diese Abfrage wird mit CTE, die nicht direkt sind unterstützt von ActiveRecord oder Arel. Wenn Sie dies umgestalten, um CTEs zu vermeiden, können Sie dies mit jedem von beiden tun. – PinnyM

+0

Können Sie überprüfen, ob Sie PostgreSQL verwenden? – PinnyM

+0

Ich brauche diese Funktion auch! –

Antwort

6

Sie versuchen anscheinend, die neuesten Trainingsdetails (höchste ID) zu erhalten, die dem höchsten Gewicht für jeden Benutzer entsprechen. Es scheint auch, dass Sie PostgreSQL verwenden (MySQL hat keine CTEs), korrigieren Sie mich, wenn ich falsch liege.

Wenn ja, können Sie die Verwendung von Windowing-Funktionen und vereinfachen Sie Ihre Abfrage machen:

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 

die in Active kann geschrieben werden:

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, Ich habe die ursprüngliche Frage mit mehr Informationen aktualisiert. Danke für die Hilfe. – MunkiPhD

+0

@MunkiPhD: Sie haben Recht, eine Unterabfrage zu benötigen - aktualisiert mit Arel 'from' und' sql', um die Unordnung zu umgehen. – PinnyM

Verwandte Themen