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

时间:2012-04-29 17:09:38

标签: sql ruby-on-rails-3 model rails-activerecord legacy-database

我正在将遗留数据库迁移到我的Rails应用程序(3.2.3)。原始数据库附带了很多用于报告的长SQL查询。现在,我想做的是在Rails应用程序中使用sql查询,然后逐个(当时间允许时)将sql查询交换为“正确的”Rails查询。

我有一个临床模型,控制器有以下代码:

 @clinical_income_by_year = Clinical.find_all_by_sql(SELECT date_format(c.transactiondate,'%Y') as Year, 
                                                 date_format(c.transactiondate,'%b') as Month,
                                                 sum(c.LineBalance) as "Income"
                                                 FROM clinical c
                                                 WHERE c.Payments = 0 AND c.LineBalance <> 0
                                                 AND c.analysiscode <> 213
                                                 GROUP BY c.MonthYear;)

但是,当我运行该代码时,我会遇到一些与格式化有关的错误。

Started GET "/clinicals" for 127.0.0.1 at 2012-04-29 18:00:45 +0100

SyntaxError (/Users/dannymcclelland/Projects/premvet/app/controllers/clinicals_controller.rb:6: syntax error, unexpected tIDENTIFIER, expecting ')'
...rmat(c.transactiondate,'%Y') as Year, 
...                               ^
/Users/dannymcclelland/Projects/premvet/app/controllers/clinicals_controller.rb:7: syntax error, unexpected tIDENTIFIER, expecting keyword_end
...rmat(c.transactiondate,'%b') as Month,
...                               ^
/Users/dannymcclelland/Projects/premvet/app/controllers/clinicals_controller.rb:8: syntax error, unexpected tIDENTIFIER, expecting keyword_end
...          sum(c.LineBalance) as "Income"
...                               ^
/Users/dannymcclelland/Projects/premvet/app/controllers/clinicals_controller.rb:10: syntax error, unexpected tCONSTANT, expecting keyword_end
...       WHERE c.Payments = 0 AND c.LineBalance <> 0
...                               ^
/Users/dannymcclelland/Projects/premvet/app/controllers/clinicals_controller.rb:10: syntax error, unexpected '>'
...yments = 0 AND c.LineBalance <> 0
...                               ^
/Users/dannymcclelland/Projects/premvet/app/controllers/clinicals_controller.rb:11: syntax error, unexpected '>'
...          AND c.analysiscode <> 213
...                               ^

在将sql查询导入控制器之前,我应该对sql查询做些什么吗?虽然查询可能有问题(它是在很久以前编写的),但它直接在数据库中运行时确实可以正常工作。它返回一个如下数组:

----------------------------------------------
|  Year      |    Month     |     Income     |
----------------------------------------------
----------------------------------------------
|  2012      |    January   |   20,000       |
|  2012      |    February  |   20,000       |
|  2012      |    March     |   20,000       |
|  2012      |    April     |   20,000       |
----------------------------------------------
etc..

任何帮助,建议或一般指示都将不胜感激!

我正在阅读http://guides.rubyonrails.org/active_record_querying.html尝试将sql查询转换为正确的Rails查询。

到目前为止,我已经匹配了倒数第二行:

AND c.analysiscode <> 213

@clinical_income_by_year = Clinical.where("AnalysisCode != 213")
宝贝的步骤!

更新

由于Rails指南网站,我现在已经对过滤进行了排序,但我仍然坚持对sql查询进行分组和求和。到目前为止,我有以下内容:

@clinical_income_by_year = Clinical.where("AnalysisCode != 213 AND Payments != 0 AND LineBalance != 0").page(params[:page]).per_page(15)

我正在努力构建以下两行sql查询:

sum(c.LineBalance) as "Income"

GROUP BY c.MonthYear;)

我的观看代码如下所示:

<% @clinical_income_by_year.each do |clinical| %>
  <tr>
    <td><%= clinical.TransactionDate.strftime("%Y") %></td>
    <td><%= clinical.TransactionDate.strftime("%B") %></td>
    <td><%= Clinical.sum(:LineBalance) %></td>
  </tr>    
  <% end %>
</table>
  <%= will_paginate @clinical_income_by_year %>

1 个答案:

答案 0 :(得分:14)

Ruby解析器不理解SQL,您需要使用字符串:

@clinical_income_by_year = Clinical.find_by_sql(%q{ ... })

我建议您使用%q%Q(如果需要插值),这样您就不必担心嵌入式引号了。您还应该将其移动到模型中的类方法中,以防止您的控制器担心不属于他们业务的事情,这也可以让您轻松访问connection.quote和朋友,以便您可以正确使用字符串插值:

find_by_sql(%Q{
    select ...
    from ...
    where x = #{connection.quote(some_string)}
})

另外,SQL中的分号:

GROUP BY c.MonthYear;})

没有必要。有些数据库会让它通过,但无论如何你应该摆脱它。

根据您的数据库,标识符(表名,列名,......)应该不区分大小写(除非某些可恶的人在创建时引用它们),因此您可以使用小写列名来制作事情更适合Rails。

另请注意,某些数据库不喜欢GROUP BY,因为SELECT中的列没有聚合或分组,因此对于每个组使用c.transactiondate存在歧义。


查询的更“Railsy”版本看起来像这样:

@c = Clinical.select(%q{date_format(transactiondate, '%Y') as year, date_format(transactiondate, '%b') as month, sum(LineBalance) as income})
             .where(:payments => 0)
             .where('linebalance <> ?', 0)
             .where('analysiscode <> ?', 213)
             .group(:monthyear)

然后你可以做这样的事情:

@c.each do |c|
    puts c.year
    puts c.month
    puts c.income
end

访问结果。你可以通过将日期修改推送到Ruby来简化一点:

@c = Clinical.select(%q{c.transactiondate, sum(c.LineBalance) as income})
             .where(:payments => 0)
             .where('linebalance <> ?', 0)
             .where('analysiscode <> ?', 213)
             .group(:monthyear)

然后在Ruby中拆分c.transactiondate而不是调用c.yearc.month