Come posso vedere l'SQL che verrà generato da una determinata query ActiveRecord in Ruby on Rails


116

Vorrei vedere l'istruzione SQL generata da una determinata query ActiveRecord. Riconosco di poter ottenere queste informazioni dal registro dopo che la query è stata inviata, ma mi chiedo se esiste un metodo che può essere richiamato e ActiveRecord Query.

Per esempio:

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

Vorrei aprire la console irb e aggiungere un metodo alla fine che mostrasse l'SQL che questa query genererà, ma non necessariamente eseguirà la query.

Risposte:


12

L'ultima volta che ho provato a farlo non c'era un modo ufficiale per farlo. Ho fatto ricorso alla funzione che findei suoi amici usano per generare direttamente le loro query. È un'API privata, quindi c'è un enorme rischio che Rails 3 la rompa completamente, ma per il debug è una soluzione accettabile.

Il metodo è construct_finder_sql(options)( lib/active_record/base.rb:1681) che dovrai usare sendperché è privato.

Modifica : è construct_finder_sql stato rimosso in Rails 5.1.0.beta1 .


1
Questo è vicino a quello che sto pensando. Immagino che una persona potrebbe scrivere un plugin che farebbe qualcosa come: SampleModel.find (: all,: select => "DISTINCT (*)" date,: conditions => [" > # {self.date}"],: limit => 1 ,: order => ' date',: group => " date") .show_generated_sql e fai chiamare il metodo construct_finder_sql.
rswolff

1
Con DataMapper potresti perché non esegue subito la query. ActiveRecord invece esegue la query immediatamente. show_generated_sqlagirà su un set di dati già recuperato da find.
John F. Miller,

4
In Rails 3 construct_finder_sqlviene effettivamente rimosso
Veger

190

Simile a penger, ma funziona in qualsiasi momento nella console anche dopo che le classi sono state caricate e il logger è stato memorizzato nella cache:

Per Rails 2:

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

Per Rails 3.0.x:

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

Per Rails> = 3.1.0 questo è già fatto di default nelle console. Nel caso sia troppo rumoroso e vuoi spegnerlo puoi fare:

ActiveRecord::Base.logger = nil

Questo non funziona per me in rails console. Funziona solo per irb caricato direttamente dalla shell? (o è stato rimosso per i binari 3?)
Eric Hu

2
L'hanno spostato in un posto più sensato per Rails 3 ... vedi la mia versione aggiornata.
gtd

C'è un modo per farlo automaticamente ogni volta che avvio la console? Qualcosa come un gancio prima del carico?
stream7

1
@ stream7..Non so se ne hai bisogno ora, ma puoi spostare questo codice in environment.rb.. if "irb" == $0;ActiveRecord::Base.logger = Logger.new(STDOUT);end..ho ottenuto dai commenti in http://weblog.jamisbuck.org/2007/1/8/watching-activerecord-do -it-s-thing
rubyprince

Questo non risponde alla domanda: come mostrare lo sql per una determinata query durante il debug senza eseguire la query.
bradw2k

87

Attacca un puts query_object.classposto per vedere con quale tipo di oggetto stai lavorando, quindi cerca i documenti.

Ad esempio, in Rails 3.0, gli scopes usano ActiveRecord::Relationwhich has a #to_sqlmethod. Per esempio:

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

Quindi, da qualche parte puoi fare:

puts Contact.frequently_contacted.to_sql

3
Perché questa risposta non viene votata positivamente? Per Rails 3 è una risposta decisamente migliore: puoi ottenere i risultati di qualsiasi istruzione AR :: Relation semplicemente inserendo ".to_sql" alla fine. Questa domanda fornisce anche la risposta: stackoverflow.com/questions/3814738/…
Steve Midgley

5
Attenzione: non otterrai tutto l'SQL se hai un includesrapporto nella tua relazione
Barry Kelly

3
@BarryKelly Perché è così?
Vishnu Narang

25

Questa potrebbe essere una vecchia domanda ma io uso:

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

Il explainmetodo fornirà un'istruzione SQL piuttosto dettagliata su cosa farà


3
Eseguirà anche la query, che potrebbe non essere ciò che le persone vogliono, solo per la cronaca.
Ibrahim

22

basta usare il to_sqlmetodo e produrrà la query sql che verrà eseguita. funziona su una relazione record attiva.

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

quando lo fai find, non funzionerà, quindi dovrai aggiungere quell'ID manualmente alla query o eseguirlo usando where.

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

11

Questo è ciò che di solito faccio per ottenere SQL generato nella console

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

Devi farlo quando avvii per la prima volta la console, se lo fai dopo aver digitato del codice, non sembra funzionare

Non posso davvero prendermi il merito di questo, l'ho trovato molto tempo fa dal blog di qualcuno e non ricordo di chi sia.


1
Sono abbastanza sicuro che fosse il blog di Jamis Buck: weblog.jamisbuck.org/2007/1/8/…
rswolff

1
Non sono sicuro che sia dovuto a Rails 2.3 o qualcosa nel mio ambiente, ma questo non funziona per me. Vedi la mia risposta di seguito.
gtd

Questa è un'ottima soluzione IMO.
Benjamin Atkin

10

Crea un file .irbrc nella tua directory home e incollalo in:

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

Questo produrrà istruzioni SQL nella tua sessione irb mentre procedi.

EDIT: Mi dispiace che eseguirà ancora la query, ma è il più vicino che io conosca.

EDIT: Ora con arel, puoi creare ambiti / metodi fintanto che l'oggetto restituisce ActiveRecord :: Relation e chiama .to_sql su di esso e metterà fuori lo sql che verrà eseguito.


Questo è quello che ho fatto, ma sono più interessato a vedere semplicemente l'SQL proiettato che la query ActiveRecord genererà. Sono sorpreso che non ci sia un modo semplice per farlo ...
rswolff

5

Il mio modo tipico per vedere quale sql usa è introdurre un "bug" nello sql, quindi riceverai un messaggio di errore sputato al normale logger (e allo schermo web) che ha lo sql in questione. Non c'è bisogno di trovare dove stdout sta andando ...


1
Soluzione spettacolare ma più veloce :)
Ján Janočko

2

Prova il plugin show_sql . Il plug-in consente di stampare l'SQL senza eseguirlo

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

1
Sembra che il collegamento sia stato interrotto (errore 404). Probabilmente, Ryan Bigg ha cancellato il plugin.
DNNX

1

È possibile modificare il metodo di log della connessione per sollevare un'eccezione, impedendo l'esecuzione della query.

È un trucco totale, ma sembra funzionare per me (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

0

In Rails 3 puoi aggiungere questa riga a config / environment / development.rb

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

Tuttavia eseguirà la query. Ma la metà ha ricevuto risposta:

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.