回答

收藏

在Rails 3应用程序中使用原始SQL查询?

技术问答 技术问答 200 人阅读 | 0 人回复 | 2023-09-14

我正在将旧数据库迁移到我的Rails应用程序(3.2.3)中。原始数据库带有相当多的长SQL查询报表。就目前而言,我想使用Rails应用程序中的sql查询,然后(在时间允许的情况下)将sql查询一一交换为“适当的”
; {4 q% j! M& i3 MRails查询。
3 |: Q; ]( m  y我有一个临床模型,控制器具有以下代码:/ A! q3 L8 M2 f! ^, r
@clinical_income_by_year = Clinical.find_all_by_sql(SELECT date_format(c.transactiondate,'%Y') as Year, ) ]  j; s( ~+ }4 G9 ^. @; ]
                                                 date_format(c.transactiondate,'%b') as Month,: U& o$ a1 t& |  Y2 U- g& w: O; Q0 J
                                                 sum(c.LineBalance) as "Income"7 L% x; D6 ~+ ~, H! W
                                                 FROM clinical c4 W* B" Y; O3 }
                                                 WHERE c.Payments = 0 AND c.LineBalance  0
0 o/ B( J4 D1 s0 r) X                                                 AND c.analysiscode  213
2 g8 |" ]! U7 v1 p$ x% h) x) b                                                 GROUP BY c.MonthYear;)
. z: _  g6 y+ g! X; F9 ~& Q& ?/ K但是,当我运行该代码时,会遇到一些与格式有关的错误。
+ Y6 k% @5 R, K$ N* TStarted GET "/clinicals" for 127.0.0.1 at 2012-04-29 18:00:45 +0100
9 k; s4 r9 _1 q7 B" XSyntaxError (/Users/dannymcclelland/Projects/premvet/app/controllers/clinicals_controller.rb:6: syntax error, unexpected tIDENTIFIER, expecting ')'/ X* Q, ]; H3 \
...rmat(c.transactiondate,'%Y') as Year,
) u! I3 J5 r( o. `...                               ^
7 w8 Y/ J9 u& J; ~8 _/Users/dannymcclelland/Projects/premvet/app/controllers/clinicals_controller.rb:7: syntax error, unexpected tIDENTIFIER, expecting keyword_end0 `  o" E" t$ s2 ~5 ~
...rmat(c.transactiondate,'%b') as Month,% y. F0 A% s( l' V+ \/ C5 b3 L1 |
...                               ^
( @2 Y1 o& N: ^! i5 Z6 |/Users/dannymcclelland/Projects/premvet/app/controllers/clinicals_controller.rb:8: syntax error, unexpected tIDENTIFIER, expecting keyword_end& K. C. h6 H+ l4 d; A5 R% v
...          sum(c.LineBalance) as "Income"
0 l& _8 S8 o; g! A...                               ^
& a" H8 K" o  A3 K9 u/Users/dannymcclelland/Projects/premvet/app/controllers/clinicals_controller.rb:10: syntax error, unexpected tCONSTANT, expecting keyword_end3 W: G5 ]7 a7 D3 D5 {) u
...       WHERE c.Payments = 0 AND c.LineBalance  0
& I2 k2 o  b% X' _6 z...                               ^
. y1 O* {, |! T' o# o/Users/dannymcclelland/Projects/premvet/app/controllers/clinicals_controller.rb:10: syntax error, unexpected '>'
! }$ O5 L' Z3 B: }+ f* ^+ [...yments = 0 AND c.LineBalance  0
4 m8 r) [$ w" z$ J& n' Q...                               ^
2 V4 L" y! i7 M8 X  ]. L1 V/Users/dannymcclelland/Projects/premvet/app/controllers/clinicals_controller.rb:11: syntax error, unexpected '>'% P$ y. e9 ]0 B1 v! a, ]. B) J& G; ?
...          AND c.analysiscode  213. ^! r# L  N7 R+ S  d4 \
...                               ^
+ f& o  V7 y9 g- Y在将它导入控制器之前,我应该对sql查询做些什么?尽管查询可能有问题(它是在很早以前编写的),但是当直接在数据库中运行时,它确实可以按预期工作。它返回一个像这样的数组:6 G( `  c; R" B! Q+ [- B
----------------------------------------------
+ N/ Y  a3 r' I6 g" X# j|  Year      |    Month     |     Income     |0 U0 M- A& A' D2 P* T8 o9 A8 j
----------------------------------------------
+ a8 ?$ s7 p  R' L0 @----------------------------------------------3 B, B( L: x( p% Y  i
|  2012      |    January   |   20,000       |
2 k+ }0 G; w4 x% B, w|  2012      |    February  |   20,000       |. ]8 t; ]! b2 t9 o5 l0 q2 F
|  2012      |    March     |   20,000       |
! H/ \2 a/ f- U|  2012      |    April     |   20,000       |
0 P- c8 e4 ]* e: c----------------------------------------------
1 U: e, N' F7 _; j6 z$ betc..
; n* c9 |9 Z0 y" I$ p2 P' A9 ?任何帮助,建议或一般指示将不胜感激!+ C5 N$ e4 C* ]3 t; c! U* t
我正在阅读http://guides.rubyonrails.org/active_record_querying.html,尝试将sql查询转换为正确的Rails查询。; e; f7 e- f5 p  Q3 I! }' Z
到目前为止,我已经匹配了倒数第二行:0 a1 }/ R8 x% e* {* r7 ^/ D* p! k
AND c.analysiscode  213- D! B+ W8 |1 C
+ y5 p& w! ~, T. ^
@clinical_income_by_year = Clinical.where("AnalysisCode != 213")
* t" T" B; @2 P宝贝的步骤!8 F9 r! m1 `  s
更新
9 y4 a+ V9 `: @6 P8 K0 E: E# ]感谢Rails指南站点,我现在已经对筛选进行了排序,但是我被困在sql查询的分组和求和部分中。到目前为止,我有以下内容:- r) c! V) _- r$ n7 x% K, G. ?
@clinical_income_by_year = Clinical.where("AnalysisCode != 213 AND Payments != 0 AND LineBalance != 0").page(params[:page]).per_page(15)
. \# |, k1 V3 v+ u$ C3 Y) Z我正在努力构建以下两行sql查询:- L% B5 ]9 h; i! s" g- X
sum(c.LineBalance) as "Income"! j1 k  T  V+ X* C  r
+ o! z  g$ B- a% ]1 ?
GROUP BY c.MonthYear;)
0 @9 H3 U9 G' k1 [4 Y( J$ P8 C5 ]我的视图代码如下所示:
3 x# T) I1 }  m9 O8 [, X& \
/ N  y$ N- U& X0 r+ A  [tr]
0 l! `8 @5 R4 d: l' c    [td][/td]  N4 ~, L7 j3 l; o! O" Q7 U4 C
    [td][/td]
( b4 r1 q% _" O5 m    [td][/td]( p1 D* u5 F2 R; T% h
  [/tr]   
9 e- k, U  e! ?, o) N) o  / ~$ a$ c9 p1 c2 a) X# y
[/table]% v7 _& k# u' w: t# m
  # Z. J2 B0 h0 k8 Y4 ?
                0 K- E- ^. v: x6 \! N* _7 u
解决方案:
4 b2 H. w0 ~8 T                , J! y8 y8 @+ f- B

7 \/ Y4 {4 j0 M! v2 k( _
1 ^/ |% L5 A) B                Ruby解析器不理解SQL,您需要使用一个字符串:
8 D5 w/ B' `* F4 n@clinical_income_by_year = Clinical.find_by_sql(%q{ ... })7 K. U! v- h* M, w5 _1 I! S
我建议为此使用%q或%Q(如果需要插值),这样就不必担心嵌入的引号了。您还应该将其移入模型的类方法中,以使您的控制器不必担心与他们无关的事情,这也将使您易于访问connection.quote并成为朋友,以便您可以正确使用字符串插值:( D8 H9 C6 D# R3 D
find_by_sql(%Q{
  G+ u, c8 M3 b9 [9 ~    select ...6 E/ I' L- K4 Y1 c
    from ...
; Q9 }$ Z2 |+ z1 j0 J& r    where x = #{connection.quote(some_string)}. K! W* J7 t2 r
})) a7 ?4 e& S; C: ?( K: H
另外,SQL中的分号:  K% j2 z5 r* L( e
GROUP BY c.MonthYear;})
+ x9 @8 k. E. s6 L4 [没必要 一些数据库会允许它通过,但是无论如何您都应该摆脱它。
, ~4 e4 J% b+ b取决于您的数据库,标识符(表名,列名等)应不区分大小写(除非某些可恶的人在创建它们时将其引用),因此您可以使用小写的列名使内容适合Rails更好。+ f$ r+ v/ d/ c4 ~8 l; F
还要注意,有些数据库不喜欢GROUP BY,因为SELECT中的列没有聚合或分组,因此c.transactiondate每个组使用哪个列含糊不清。
- Z; d( g$ Z3 z& u  O
+ E& Q2 q# `) \/ I% v4 Y* O" F查询的更多“ Railsy”版本将如下所示:/ F* `: T/ u" F% {
@c = Clinical.select(%q{date_format(transactiondate, '%Y') as year, date_format(transactiondate, '%b') as month, sum(LineBalance) as income})
( B# G1 s- `2 C5 h8 K" H             .where(:payments => 0)1 F7 _/ p  D9 m
             .where('linebalance  ?', 0)
" |8 q* h" V3 k- l             .where('analysiscode  ?', 213)3 V! f/ g- b6 \- f2 ?8 D2 ^; B0 L% j# G
             .group(:monthyear)
+ q; d3 |- ^4 t0 I; v然后,您可以执行以下操作:* w# O7 I5 n3 D7 u: h$ g
@c.each do |c|
1 f8 n5 T0 F* ]2 r* n4 w    puts c.year4 n+ y6 `3 g6 T+ n& h  A! Z
    puts c.month! v8 k. H& I" W; \  S0 b7 c- V
    puts c.income& @, R) `, u  _) R5 f
end4 p. j% v- C* p2 u! B- P
访问结果。您还可以通过将日期处理推入Ruby来简化一点:' ?$ C, d, H* B: H
@c = Clinical.select(%q{c.transactiondate, sum(c.LineBalance) as income})
3 P4 R9 A5 c: D% C$ d             .where(:payments => 0)1 q! K; Z% B# e, L8 L: h- I; d1 g
             .where('linebalance  ?', 0)
6 A4 \% e9 O3 k( w- X* j8 f, o             .where('analysiscode  ?', 213)+ Q( }/ f6 s8 |7 x
             .group(:monthyear)
- X+ M4 J' M* Y然后c.transactiondate用Ruby拆开,而不是调用c.yearand c.month。
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则