Wie kann ich die SQL sehen, die von einer bestimmten ActiveRecord-Abfrage in Ruby on Rails generiert wird?

116

Ich möchte die SQL-Anweisung sehen, die eine bestimmte ActiveRecord-Abfrage generiert. Ich erkenne, dass ich diese Informationen aus dem Protokoll abrufen kann, nachdem die Abfrage ausgegeben wurde, aber ich frage mich, ob es eine Methode gibt, die aufgerufen werden kann, und ActiveRecord Query.

Beispielsweise:

SampleModel.find(:all, :select => "DISTINCT(*)", :conditions => ["`date` > #{self.date}"], :limit => 1, :order => '`date`', :group => "`date`")

Ich möchte die irb-Konsole öffnen und am Ende eine Methode anheften, die die SQL anzeigt, die diese Abfrage generiert, aber nicht unbedingt die Abfrage ausführt.

rswolff
quelle

Antworten:

12

Als ich das letzte Mal versuchte, gab es keinen offiziellen Weg, dies zu tun. Ich habe auf die Funktion zurückgegriffen, mit der findund ihre Freunde ihre Abfragen direkt generieren. Da es sich um eine private API handelt, besteht ein großes Risiko, dass Rails 3 sie vollständig beschädigt, aber für das Debuggen ist es eine gute Lösung.

Die Methode ist construct_finder_sql(options)( lib/active_record/base.rb:1681), die Sie verwenden müssen, sendda sie privat ist.

Bearbeiten : construct_finder_sql wurde in Rails 5.1.0.beta1 entfernt .

John F. Miller
quelle
1
Das kommt dem nahe, was ich denke. Ich denke, eine Person könnte ein Plugin schreiben, das ungefähr so ​​funktioniert: SampleModel.find (: all ,: select => "DISTINCT (*)" ,: Bedingungen => [" date> # {self.date}"] ,: limit => 1 ,: order => ' date' ,: group => " date") .show_generated_sql und lassen Sie dies die Methode construct_finder_sql aufrufen.
Rswolff
1
Mit DataMapper können Sie dies tun, da die Abfrage nicht sofort ausgeführt wird. ActiveRecord hingegen führt die Abfrage sofort aus. show_generated_sqlwird auf einen bereits abgerufenen Datensatz von reagieren find.
John F. Miller
4
In Rails 3 construct_finder_sqlwird tatsächlich entfernt
Veger
190

Ähnlich wie bei Penger, funktioniert jedoch jederzeit in der Konsole, auch nachdem Klassen geladen und der Logger zwischengespeichert wurden:

Für Schienen 2:

ActiveRecord::Base.connection.instance_variable_set :@logger, Logger.new(STDOUT)

Für Schienen 3.0.x:

ActiveRecord::Base.logger = Logger.new(STDOUT)

Für Rails> = 3.1.0 erfolgt dies standardmäßig bereits in Konsolen. Falls es zu laut ist und Sie es ausschalten möchten, können Sie Folgendes tun:

ActiveRecord::Base.logger = nil
gtd
quelle
Das funktioniert bei mir nicht rails console. Funktioniert es nur für irb, die direkt von der Shell geladen werden? (oder wurde es für Schienen 3 entfernt?)
Eric Hu
2
Sie haben es an einen vernünftigeren Ort für Rails 3 verschoben ... siehe meine aktualisierte Version.
GTD
Gibt es eine Möglichkeit, dies jedes Mal automatisch zu tun, wenn ich die Konsole starte? So etwas wie ein Vorladehaken?
Stream7
1
@ stream7..Ich weiß nicht, ob du das jetzt brauchst, aber du kannst diesen Code nach environment.rb.. if "irb" == $0;ActiveRecord::Base.logger = Logger.new(STDOUT);end..got dies aus Kommentaren in http://weblog.jamisbuck.org/2007/1/8/watching-activerecord-do verschieben -es-s-Ding
Rubyprince
Dies beantwortet nicht die Frage: Wie wird die SQL für eine bestimmte Abfrage beim Debuggen angezeigt, ohne dass die Abfrage ausgeführt wird?
Bradw2k
87

Kleben Sie puts query_object.classirgendwo ein, um zu sehen, mit welcher Art von Objekt Sie arbeiten, und suchen Sie dann in den Dokumenten.

In Rails 3.0 verwenden beispielsweise Bereiche ActiveRecord::Relationeine #to_sqlMethode. Beispielsweise:

class Contact < ActiveRecord::Base
  scope :frequently_contacted, where('messages_count > 10000')
end

Dann können Sie irgendwo etwas tun:

puts Contact.frequently_contacted.to_sql
Pfadabhängig
quelle
3
Warum wird diese Antwort nicht positiv bewertet? Für Rails 3 ist es eine viel bessere Antwort - Sie können die Ergebnisse jeder AR :: Relation-Anweisung erhalten, indem Sie am Ende ".to_sql" einfügen. Diese Frage liefert auch diese Antwort: stackoverflow.com/questions/3814738/…
Steve Midgley
5
Achtung: Sie werden nicht alle SQL erhalten, wenn Sie eine includesin Ihrer Beziehung haben
Barry Kelly
3
@BarryKelly Warum ist das so?
Vishnu Narang
25

Dies mag eine alte Frage sein, aber ich benutze:

SampleModel.find(:all,
                 :select => "DISTINCT(*)",
                 :conditions => ["`date` > #{self.date}"], 
                 :limit=> 1, 
                 :order => '`date`',
                 :group => "`date`"
                 ).explain

Die explainMethode gibt eine ziemlich detaillierte SQL-Anweisung darüber, was sie tun wird

Akshat
quelle
3
Es wird auch die Abfrage ausgeführt, die möglicherweise nicht den Wünschen der Benutzer entspricht, nur für die Aufzeichnung.
Ibrahim
22

Verwenden to_sqlSie einfach die Methode und es wird die SQL-Abfrage ausgegeben, die ausgeführt wird. Es funktioniert mit einer aktiven Datensatzbeziehung.

irb(main):033:0> User.limit(10).where(:username => 'banana').to_sql
=> "SELECT  "users".* FROM "users"  WHERE "users"."username" = 'banana'
LIMIT 10"

In finddiesem Fall funktioniert es nicht. Sie müssen diese ID also manuell zur Abfrage hinzufügen oder mit where ausführen.

irb(main):037:0* User.where(id: 1).to_sql
=> "SELECT "users".* FROM "users"  WHERE "users"."id" = 1"
Itai Sagi
quelle
11

Dies ist, was ich normalerweise mache, um SQL in der Konsole zu generieren

-> script/console
Loading development environment (Rails 2.1.2)
>> ActiveRecord::Base.logger = Logger.new STDOUT
>> Event.first

Sie müssen dies tun, wenn Sie die Konsole zum ersten Mal starten. Wenn Sie dies tun, nachdem Sie Code eingegeben haben, scheint dies nicht zu funktionieren

Ich kann das nicht wirklich würdigen, habe es vor langer Zeit in einem Blog von jemandem gefunden und kann mich nicht erinnern, wer es ist.

Penger
quelle
1
Ich bin mir ziemlich sicher, dass es Jamis Buck's Blog war: weblog.jamisbuck.org/2007/1/8/…
rswolff
1
Ich bin nicht sicher, ob es an Rails 2.3 oder etwas in meiner Umgebung liegt, aber das funktioniert bei mir nicht. Siehe meine Antwort unten.
GTD
Dies ist eine großartige Lösung, IMO.
Benjamin Atkin
10

Erstellen Sie eine .irbrc-Datei in Ihrem Home-Verzeichnis und fügen Sie diese ein in:

if ENV.include?('RAILS_ENV') && !Object.const_defined?('RAILS_DEFAULT_LOGGER')
  require 'logger'
  RAILS_DEFAULT_LOGGER = Logger.new(STDOUT)
end

Dadurch werden SQL-Anweisungen in Ihre irb-Sitzung ausgegeben, während Sie fortfahren.

EDIT: Tut mir leid, dass die Abfrage immer noch ausgeführt wird, aber es ist am nächsten, von dem ich weiß.

BEARBEITEN: Jetzt können Sie mit arel Bereiche / Methoden erstellen, solange das Objekt ActiveRecord :: Relation zurückgibt und .to_sql darauf aufruft, und es wird die SQL ausgeben, die ausgeführt werden soll.

Nitecoder
quelle
Dies ist, was ich getan habe, aber ich bin mehr daran interessiert, einfach das projizierte SQL zu sehen, das die ActiveRecord-Abfrage generiert. Ich bin überrascht, dass es keinen einfachen Weg gibt, dies zu tun ...
rswolff
5

Meine typische Methode, um zu sehen, welche SQL verwendet wird, besteht darin, einen "Fehler" in die SQL einzuführen. Anschließend wird eine Fehlermeldung an den normalen Logger (und den Webbildschirm) gesendet, auf der sich die betreffende SQL befindet. Sie müssen nicht herausfinden, wohin stdout geht ...

Rogerdpack
quelle
1
Spektakuläre aber schnellste Lösung :)
Ján Janočko
2

Probieren Sie das show_sql-Plugin aus . Mit dem Plugin können Sie SQL drucken, ohne es auszuführen

SampleModel.sql(:select => "DISTINCT(*)", :conditions => ["`date` > #{self.date}"], :limit => 1, :order => '`date`', :group => "`date`")
Harish Shetty
quelle
1
Es sieht so aus, als ob der Link unterbrochen wurde (404-Fehler). Wahrscheinlich hat Ryan Bigg das Plugin gelöscht.
DNNX
1

Sie können die Protokollmethode der Verbindung ändern, um eine Ausnahme auszulösen und zu verhindern, dass die Abfrage ausgeführt wird.

Es ist ein totaler Hack, aber es scheint für mich zu funktionieren (Rails 2.2.2, MySQL):

module ActiveRecord
  module ConnectionAdapters
    class AbstractAdapter
      def log_with_raise(sql, name, &block)
        puts sql
        raise 'aborting select' if caller.any? { |l| l =~ /`select'/ }
        log_without_raise(sql, name, &block)
      end
      alias_method_chain :log, :raise
    end
  end
end
ebenfalls
quelle
0

In Rails 3 können Sie diese Zeile zur Datei config / environment / development.rb hinzufügen

config.active_record.logger = Logger.new(STDOUT)

Die Abfrage wird jedoch ausgeführt. Aber die Hälfte wurde beantwortet:

Timbinous
quelle