7

Ich arbeite an der Migration einer Legacy-Datenbank in meine Rails-Anwendung (3.2.3). Die ursprüngliche Datenbank enthält einige lange SQL-Abfragen für Berichte. Für den Moment, was ich tun möchte, verwenden Sie die SQL-Abfragen in der Rails-Anwendung und dann nacheinander (wenn die Zeit es erlaubt) tauschen Sie die SQL-Abfragen zu "richtigen" Rails-Abfragen.Verwenden von Raw SQL-Abfragen in Rails 3-Anwendung?

Ich habe ein klinisches Modell und die Controller hat den folgenden Code:

@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;) 

Allerdings, wenn ich diesen Code ausführen bekomme ich ein paar Fehler bei der Formatierung zu tun.

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 
...        ^

Gibt es etwas, was ich tun sollte, um die SQL-Abfrage vor dem Importieren in den Controller? Obwohl es möglich ist, dass etwas mit der Abfrage nicht stimmt (es wurde vor einiger Zeit geschrieben), funktioniert es wie erwartet, wenn es direkt in der Datenbank ausgeführt wird. Es gibt ein Array wie folgt zurück:

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

Jede Hilfe, Beratung oder allgemeine Hinweise wäre zu schätzen!

Ich lese durch http://guides.rubyonrails.org/active_record_querying.html versuchen, die SQL-Abfrage in eine korrekte Rails-Abfrage zu konvertieren.

AND c.analysiscode <> 213 

mit

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

Baby-Schritte:

Bisher habe ich die vorletzte Zeile abgestimmt!

UPDATE

ich die Filterung habe sortiert jetzt, dank der Führung Website Rails, aber ich bin auf der Gruppierung und Summen Teil der SQL-Abfrage fest. Ich habe nach dem bisher:

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

Ich kämpfe in den folgenden zwei Zeilen der SQL-Abfrage zu erstellen:

sum(c.LineBalance) as "Income" 

und

GROUP BY c.MonthYear;) 

Meine Ansicht Code wie folgt aussehen dies:

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

Antwort

14

Die R uby Parser versteht nicht, SQL, müssen Sie eine Zeichenfolge verwenden:

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

Ich würde empfehlen, mit %q oder %Q (wenn Sie Interpolation benötigen) für diese so, dass Sie sich über eingebettete Anführungszeichen keine Sorge so sehr.Sie sollten dies auch in eine Klassenmethode im Modell verschieben, damit Ihre Controller sich nicht um Dinge kümmern müssen, die ihnen nichts angehen. Dadurch erhalten Sie auch einfachen Zugriff auf connection.quote und Freunde, so dass Sie die String-Interpolation richtig verwenden können:

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

auch das Semikolon in Ihrer SQL:

GROUP BY c.MonthYear;}) 

ist nicht erforderlich. Einige Datenbanken werden es durchlassen, aber Sie sollten es trotzdem loswerden.

Je nach Ihrer Datenbank sollten die Bezeichner (Tabellennamen, Spaltennamen, ...) zwischen Groß- und Kleinschreibung unterscheiden (es sei denn, eine hasserfüllte Person hat sie bei der Erstellung zitiert), so dass Sie Kleinbuchstaben verwenden können Dinge besser in Rails einpassen.

Beachten Sie auch, dass einige Datenbanken dieses GROUP BY nicht mögen, da Sie Spalten in Ihrem SELECT haben, die nicht aggregiert oder gruppiert sind. Daher gibt es Unklarheiten darüber, welche c.transactiondate für jede Gruppe verwendet werden sollen.


Eine "Railsy" Version Ihrer Abfrage würde wie folgt aussehen:

@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) 

Dann Sie Dinge wie dies tun könnte:

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

die Ergebnisse zuzugreifen. Sie könnten auch ein wenig, indem Sie den Zeitpunkt Mangeln in Rubin vereinfachen:

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

ziehen dann auseinander c.transactiondate in Ruby statt c.year und c.month aufrufen.

+0

Da ich die Route der Konvertierung der SQL-Abfrage in eine Rails-Abfrage gestartet habe, würden Sie empfehlen, SQL-Abfragen fortzusetzen oder SQL-Abfragen in Modellen und Controllern zu verwenden? – dannymcc

+1

@dannymcc: Die Verwendung der Rails-Methode wird normalerweise empfohlen, aber manchmal ist SQL erforderlich, wenn Sie "erweiterte" Datenbankfunktionen wie gespeicherte Prozeduren, Fensterfunktionen, abgeleitete Tabellen, CTEs usw. verwenden, dann müssen Sie eine Menge Rohdaten schreiben SQL, um den Job zu erledigen; Wenn alle Ihre Abfragen einfach 'Select * von t1 where ...' Abfragen sind, dann sind Sie wahrscheinlich besser mit den ActiveRecord-Methoden. Es ist ein Urteilsspruch, benutze, was dir und den Leuten, die deinen Code pflegen, klarer ist. –

+0

Ich denke, mit ActiveRecord Methoden sind für mich klarer beim Lesen, aber sie zu schreiben ist genauso komplex wie das Schreiben von SQL-Abfragen für mich! Danke für den Hinweis. – dannymcc