【问题标题】:Using raw sql queries in Rails 3 application?在 Rails 3 应用程序中使用原始 sql 查询?
【发布时间】:2012-04-29 17:09:37
【问题描述】:

我正在将旧数据库迁移到我的 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 查询导入控制器之前,我应该对它做些什么吗?虽然查询可能有问题(它是很久以前写的),但当直接在数据库中运行时,它确实可以按预期工作。它返回一个这样的数组:

----------------------------------------------
|  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 %>

【问题讨论】:

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


    【解决方案1】:

    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

    【讨论】:

    • 在我开始将 sql 查询转换为 Rails 查询的过程中,您会建议继续使用 sql 查询还是在模型和控制器中使用 sql 查询?
    • @dannymcc:通常建议使用 Rails 方法,但有时需要使用 SQL,如果您使用“高级”数据库功能,例如存储过程、窗口函数、派生表、CTE 等,那么您将必须编写大量原始 SQL 才能完成工作;如果您的所有查询都是简单的select * from t1 where... 查询,那么您可能最好使用 ActiveRecord 方法。这是一个判断调用,使用对您和维护您的代码的人来说更清楚的任何内容。
    • 我认为阅读 ActiveRecord 方法对我来说更清晰,但编写它们就像为我编写原始 sql 查询一样复杂!感谢您的建议。
    • +1,但我也建议使用参数而不是插值。
    • @dwerner:如果你必须使用原始 SQL,你就会被插值困住,因为 AR 对数据库的态度如此糟糕。
    猜你喜欢
    • 1970-01-01
    • 2011-12-11
    • 1970-01-01
    • 2021-12-05
    • 1970-01-01
    • 2021-10-10
    • 2018-06-02
    • 2016-10-08
    • 1970-01-01
    相关资源
    最近更新 更多