¿Cómo puedo ver el SQL que generará una consulta de ActiveRecord determinada en Ruby on Rails?

116

Me gustaría ver la declaración SQL que generará una consulta ActiveRecord determinada. Reconozco que puedo obtener esta información del registro después de que se haya emitido la consulta, pero me pregunto si hay un método al que se pueda llamar y ActiveRecord Query.

Por ejemplo:

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

Me gustaría abrir la consola irb y agregar un método al final que mostraría el SQL que generará esta consulta, pero no necesariamente ejecutará la consulta.

rswolff
fuente

Respuestas:

12

La última vez que intenté hacer esto, no había una forma oficial de hacerlo. Recurrí a usar la función que findusan y sus amigos para generar sus consultas directamente. Es una API privada, por lo que existe un gran riesgo de que Rails 3 la rompa por completo, pero para la depuración, es una buena solución.

El método es construct_finder_sql(options)( lib/active_record/base.rb:1681) que tendrá que usar sendporque es privado.

Editar : construct_finder_sql se eliminó en Rails 5.1.0.beta1 .

John F. Miller
fuente
1
Esto se acerca a lo que estoy pensando. Supongo que una persona podría escribir un complemento que haría algo como: SampleModel.find (: all,: select => "DISTINCT (*)" date,: conditions => [" > # {self.date}"],: limit => 1 ,: order => ' date',: group => " date") .show_generated_sql y haga que esto llame al método construct_finder_sql.
rswolff
1
Con DataMapper podría hacerlo porque no ejecuta la consulta de inmediato. ActiveRecord, por otro lado, ejecuta la consulta inmediatamente. show_generated_sqlactuará sobre un conjunto de datos ya recuperado de find.
John F. Miller
4
De hecho, en Rails 3 construct_finder_sqlse elimina
Veger
190

Similar a Penger, pero funciona en cualquier momento en la consola, incluso después de que se hayan cargado las clases y el registrador se haya almacenado en caché:

Para rieles 2:

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

Para Rails 3.0.x:

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

Para Rails> = 3.1.0 esto ya se hace de forma predeterminada en las consolas. En caso de que sea demasiado ruidoso y desee apagarlo , puede hacer:

ActiveRecord::Base.logger = nil
gtd
fuente
Esto no me está funcionando en rails console. ¿Funciona solo para irb cargado directamente desde el shell? (¿O se eliminó para los rieles 3?)
Eric Hu
2
Lo trasladaron a un lugar más sensato para Rails 3 ... mira mi versión actualizada.
gtd
¿Hay alguna forma de hacer esto automáticamente cada vez que inicio la consola? ¿Algo como un gancho de carga anterior?
stream7
1
@ stream7..No sé si necesitas esto ahora, pero puedes mover este código a environment.rb.. if "irb" == $0;ActiveRecord::Base.logger = Logger.new(STDOUT);end..lo obtuve de los comentarios en http://weblog.jamisbuck.org/2007/1/8/watching-activerecord-do -it-s-thing
rubyprince
Esto no responde a la pregunta: cómo mostrar el sql para una consulta en particular mientras se depura sin ejecutar la consulta.
bradw2k
87

Pegue un puts query_object.classlugar para ver con qué tipo de objeto está trabajando, luego busque los documentos.

Por ejemplo, en Rails 3.0, los alcances usan ActiveRecord::Relationque tiene un #to_sqlmétodo. Por ejemplo:

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

Entonces, en algún lugar puedes hacer:

puts Contact.frequently_contacted.to_sql
Dependiente del camino
fuente
3
¿Por qué esta respuesta no se vota a favor? Para Rails 3 es una respuesta mucho mejor: puede obtener los resultados de cualquier instrucción AR :: Relation simplemente poniendo ".to_sql" al final. Esta pregunta también proporciona esa respuesta: stackoverflow.com/questions/3814738/…
Steve Midgley
5
Cuidado: no obtendrá todo el SQL si tiene un includesen su relación
Barry Kelly
3
@BarryKelly ¿Por qué?
Vishnu Narang
25

Esta puede ser una pregunta antigua, pero yo uso:

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

El explainmétodo dará una declaración SQL bastante detallada sobre lo que va a hacer

Akshat
fuente
3
También ejecutará la consulta, que podría no ser lo que la gente quiere, solo para el registro.
Ibrahim
22

solo use el to_sqlmétodo y generará la consulta SQL que se ejecutará. funciona en una relación de registro activa.

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

al hacerlo find, no funcionará, por lo que deberá agregar esa identificación manualmente a la consulta o ejecutarla usando where.

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

Esto es lo que suelo hacer para generar SQL en la consola

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

Tienes que hacer esto cuando inicias la consola por primera vez, si lo haces después de haber escrito algún código, no parece funcionar

Realmente no puedo tomar el crédito por esto, lo encontré hace mucho tiempo en el blog de alguien y no puedo recordar de quién es.

penger
fuente
1
Estoy bastante seguro de que era el blog de Jamis Buck: weblog.jamisbuck.org/2007/1/8/…
rswolff
1
No estoy seguro de si se debe a Rails 2.3 o algo en mi entorno, pero esto no me funciona. Vea mi respuesta a continuación.
gtd
Esta es una gran solución en mi opinión.
Benjamin Atkin
10

Cree un archivo .irbrc en su directorio de inicio y péguelo en:

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

Eso generará declaraciones SQL en su sesión de irb a medida que avanza.

EDITAR: Lo siento, todavía ejecutará la consulta, pero es lo más cercano que conozco.

EDITAR: Ahora con arel, puede construir alcances / métodos siempre que el objeto devuelva ActiveRecord :: Relation y llame a .to_sql en él y saldrá el sql que se va a ejecutar.

nitecoder
fuente
Esto es lo que he estado haciendo, pero estoy más interesado en simplemente ver el SQL proyectado que generará la consulta de ActiveRecord. Me sorprende que no haya una manera sencilla de hacer esto ...
rswolff
5

Mi forma típica de ver qué sql usa es introducir un "error" en el sql, luego aparecerá un mensaje de error en el registrador normal (y la pantalla web) que tiene el sql en cuestión. No es necesario encontrar a dónde va stdout ...

Rogerdpack
fuente
1
Solución espectacular pero más rápida :)
Ján Janočko
2

Prueba el complemento show_sql . El complemento le permite imprimir el SQL sin ejecutarlo

SampleModel.sql(:select => "DISTINCT(*)", :conditions => ["`date` > #{self.date}"], :limit => 1, :order => '`date`', :group => "`date`")
Harish Shetty
fuente
1
Parece que el enlace se rompió (error 404). Probablemente, Ryan Bigg eliminó el complemento.
DNNX
1

Puede cambiar el método de registro de la conexión para generar una excepción, evitando que se ejecute la consulta.

Es un truco total, pero parece funcionar para mí (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
además
fuente
0

En Rails 3 puedes agregar esta línea a config / environment / development.rb

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

Sin embargo, ejecutará la consulta. Pero la mitad fue respondida:

Timbinoso
fuente