7

私はレガシーデータベースをRailsアプリケーション(3.2.3)に移行しようとしています。元のデータベースには、かなり長いSQLクエリーがレポート用に用意されています。今のところ、私はそれを行うには、Railsアプリケーションでsqlクエリを使用し、次に(時間が許せば)SQLクエリを '適切な' Railsクエリにスワップします。Rails 3アプリケーションでraw SQLクエリを使用していますか?

私は臨床モデルを持っていると、コントローラは、次のコードを持っている:私はそのコードを実行したとき、私は、フォーマットを行うにはいくつかのエラーを取得し、しかし

@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から、正しいRailsクエリにSQLクエリを変換しようとしています。

これまでのところ、私は最後の行に2番目に一致している:

@clinical_income_by_year = Clinical.where("AnalysisCode != 213") 

赤ちゃんの手順で

AND c.analysiscode <> 213 

を!私はフィルタリングを持って

UPDATE

は、今のRailsガイドサイトのおかげでソートされたが、私は、SQLクエリのグループ化と合計の部分にこだわっています。私はこれまで、次のいる:

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

私は、SQLクエリの次の2行に構築するために苦労している:私のビューのコードは次のように見えます

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

答えて

14

R ubyパーサを使用して、文字列を使用する必要があり、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に収める。

また、一部のデータベースでは、SELECTに集約またはグループ化されていない列があるため、グループごとに使用するc.transactiondateの曖昧さがあるため、そのGROUP BYが嫌いです。


クエリのより "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 

を。また、ルビーにマングリング日付を押して少し簡略化することができます:

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

その後、むしろc.yearc.monthを呼び出すよりも離れてc.transactiondate Rubyで引き出します。

+0

sqlクエリをRailsクエリに変換するルートを開始したので、モデルやコントローラ内でsqlクエリを使用することをお勧めしますか? – dannymcc

+1

@dannymcc:通常はRailsメソッドを使用することをお勧めしますが、ストアドプロシージャ、ウィンドウ関数、派生テーブル、CTEなどの「高度な」データベース機能を使用した場合はSQLが必要になることがあります。ジョブを完了させるためのSQL。すべてのクエリが単純な 'select * from t1 where ...'クエリであれば、おそらくActiveRecordメソッドを使う方が良いでしょう。それは判決であり、あなたとあなたのコードを維持している人々がより明確なものを使用します。 –

+0

私はActiveRecordメソッドを使って読んだほうがはっきりしていると思いますが、それらを書くことは私のために生のSQLクエリを書くのと同じくらい複雑です!アドバイスをいただきありがとうございます。 – dannymcc

関連する問題