|
如何将以下SQL查询转换为ActiveRecord关系,以便可以使用范围对其进行扩展?
) @( u8 x6 K7 K4 }3 o S: s8 p0 _WITH joined_table AS (# }) L$ h: V& H! L
SELECT workout_sets.weight AS weight,
& ^* z* M6 F" q: W4 u workouts.user_id AS user_id,
% i- H2 T* |5 P% G workouts.id AS workout_id, 2 W. d0 P9 r) ~7 l+ ?
workout_sets.id AS workout_set_id,- z% z9 s4 t$ W, a
workout_exercises.exercise_id AS exercise_id
, d4 g0 |5 U# ]- w3 D1 F FROM workouts
* g9 u' A* w. c+ A INNER JOIN workout_exercises ON workout_exercises.workout_id = workouts.id
! z* Q7 v- V5 J; a+ s s+ K INNER JOIN workout_sets ON workout_sets.workout_exercise_id = workout_exercises.id
n R; O1 Q# E. b ORDER BY workout_sets.weight DESC& N+ A; u/ I' N
), d9 N1 |: `6 ]* _$ C# F' F
sub_query AS (
. Q8 O7 C/ ~9 _ M, F! z SELECT p.user_id, MAX(weight) as weight
e$ D X" z7 ? FROM joined_table p) t$ T9 k5 z7 m
GROUP BY p.user_id* j: T( X t* Y3 J7 z/ K- \
),
! W* J7 l+ e: fresult_set AS (7 M- V* ]- X0 Q& i
SELECT MAX(x.workout_id) AS workout_id, x.user_id, x.weight, x.workout_set_id, x.exercise_id" r# } L& o* g$ v0 ]* b
FROM joined_table x# O5 z' F9 Z) z
JOIN sub_query y
) w* y1 z o9 p3 g2 k2 b ON y.user_id = x.user_id AND y.weight = x.weight
, T; o/ A/ _; X. I( w/ b4 F GROUP BY x.user_id, x.weight, x.workout_set_id, x.exercise_id$ I( {2 v+ F7 q
ORDER BY x.weight DESC)
/ P7 D0 f. A) X1 G7 `SELECT workouts.*, result_set.weight, result_set.workout_set_id, result_set.exercise_id
1 I% I$ K" f! q3 l# u/ GFROM workouts, result_set. X4 h6 J8 w1 N9 D
WHERE workouts.id = result_set.workout_id
: g) {7 P1 W6 \: K, S这是我必须尝试直接使用Arel的东西吗?# \2 {! p5 V+ r m8 e
我尝试将其分解为作用域/子查询,但是子查询上的选择最终在封闭查询中,因此引发PostgreSql错误,因为未在封闭语句中的GROUP BY或ORDER' {! Q, {* h( W' r. `" U, l! i
BY中指定该列。1 s$ e6 _0 C) r# n# I
更新: 您认为它是PostgreSql是正确的。我尝试了您的查询,但是PG::Error: ERROR: column "rownum" does$ p; a, p% v C: E4 {8 g
not exist对于直接查询和ActiveRecord等效项都抛出。
- A$ X0 x5 q# s5 R但是,当我将查询包装在单独的查询中时,它可以工作。我假设直到将选择项投影到数据集之后才创建ROW_NUMBER()。因此,以下查询有效:: `+ M, S! V, C" Y
SELECT workouts.*, t.weight, t.workout_set_id, t.exercise_id, t.row_num
$ R* ]! d+ @% k; { y t. bFROM workouts,
4 }9 z8 f& p1 F! j9 F6 S! a(SELECT workouts.id as workout_id, workout_sets.weight as weight,
7 A; Y1 c0 e! F, Q# b workout_sets.id AS workout_set_id,
6 T* m* P& C% [# l5 A workout_exercises.id AS exercise_id,& ^2 O b- s0 h
ROW_NUMBER() OVER (
1 P/ K' t2 o9 m. [8 X6 Z6 w' |+ Y PARTITION BY workouts.user_id
& F- c ]+ F. V$ Y ORDER BY workout_sets.weight DESC, workouts.id DESC ) row_num
. r0 G3 z. S" a9 N8 p FROM workouts- j( y0 P& O& H8 _9 r' j- z
JOIN workout_exercises ON workout_exercises.workout_id = workouts.id
0 Q; k* a* \0 C& v% h ]% C/ x( } JOIN workout_sets ON workout_sets.workout_exercise_id = workout_exercises.id) as t. q) S) C& g" u
WHERE workouts.id = t.workout_id AND t.row_num = 1, O: G! e: k7 K/ m
我设法对以下内容进行了按摩:
/ C! e6 O/ q; s: L% N( p. R! Q/ y+ | 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")
$ F1 Q/ F" `6 w" ?. q( _) h但是,正如您所知道的那样,这非常骇人,并且有大量的代码味道。关于如何重构的任何想法?
% z/ w6 |+ s' M" T9 ~ O3 l. G T2 j' i- s+ d2 k, f2 T
解决方案:
1 }" `1 [$ S6 w$ s
; U C' T7 x( O% u0 |; T2 T
2 a2 R' o: A# A' Y/ r- ~% \# |/ A( ^2 J& y+ s
您显然正在尝试获取与每个用户的最高体重相匹配的最新锻炼(最高ID)详细信息。似乎您正在使用PostgreSQL(MySQL没有CTE),如果我在此方面做错了,请更正我。; M' w% S: _; x+ d
如果是这样,您可以利用窗口功能并将查询简化为:
6 O8 I: O9 ^! MSELECT * FROM (
' O+ X1 x3 A$ F' g7 q SELECT workouts.*, workout_sets.weight,
3 _# N& t, ?4 a9 S5 f+ _5 U workout_sets.id AS workout_set_id,0 f; U' m0 f8 K* Y8 D
workout_exercises.id AS exercise_id,9 K2 x& Z3 A4 C; ~
ROW_NUMBER() OVER (& G! {1 s: v$ K* W1 c$ s. n' K2 A
PARTITION BY workouts.user_id
/ n. H% L0 Z/ G/ C ORDER BY workout_sets.weight DESC, workouts.id DESC ) as rowNum
' h# Q- n. X* z/ }/ v5 r, g* n FROM workouts
5 A9 E( i2 f8 `( i JOIN workout_exercises ON workout_exercises.workout_id = workouts.id
/ A( G, ], L( K: U- U JOIN workout_sets ON workout_sets.workout_exercise_id = workout_exercises.id& j3 h+ b: `6 B- P
) t
% W; A4 h; L, i5 u) q) h; YWHERE rowNum = 12 i# D% I+ H( e) \, g4 @9 S+ g8 v
在ActiveRecord中可以写成:
( N" E! T# N" l, W D+ f4 nselected_fields = :workout_sets).
9 l2 q! d' @# }; w+ o1 \& [) Y0 w, N4 i/ p select(selected_fields).to_sql
& {, t1 C. I6 q) o0 J) p0 J# {" G" j: KWorkout.select("*").from(Arel.sql("(#{subquery}) as t"))
( G. O9 c8 y5 j. a' P .where("rowNum = 1") |
|