Como posso ver o SQL que será gerado por uma determinada consulta ActiveRecord em Ruby on Rails

116

Gostaria de ver a instrução SQL que uma determinada consulta ActiveRecord irá gerar. Reconheço que posso obter essas informações do log após a consulta ter sido emitida, mas estou me perguntando se há um método que pode ser chamado e ActiveRecord Query.

Por exemplo:

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

Gostaria de abrir o console do irb e adicionar um método no final que mostre o SQL que essa consulta irá gerar, mas não necessariamente executará a consulta.

rswolff
fonte

Respostas:

12

Da última vez que tentei fazer isso, não havia uma maneira oficial de fazê-lo. Recorri a usar a função que finde seus amigos usam para gerar suas consultas diretamente. É uma API privada, então há um grande risco de que o Rails 3 vá quebrá-la totalmente, mas para depuração, é uma solução ok.

O método é construct_finder_sql(options)( lib/active_record/base.rb:1681) você terá que usar sendporque é privado.

Editar : construct_finder_sql foi removido no Rails 5.1.0.beta1 .

John F. Miller
fonte
1
Isso está perto do que estou pensando. Acho que uma pessoa poderia escrever um plugin que faria algo como: SampleModel.find (: all,: select => "DISTINCT (*)" date,: conditions => [" > # {self.date}"],: limit => 1 ,: order => ' date',: group => " date") .show_generated_sql e faça com que este chame o método construct_finder_sql.
rswolff
1
Com o DataMapper você pode, porque ele não executa a consulta imediatamente. ActiveRecord, por outro lado, executa a consulta imediatamente. show_generated_sqlestará atuando em um conjunto de dados já recuperado de find.
John F. Miller de
4
In Rails 3 construct_finder_sqlfoi realmente removido
Veger
190

Semelhante ao penger, mas funciona a qualquer momento no console, mesmo depois que as classes foram carregadas e o logger foi armazenado em cache:

Para Rails 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, isso já é feito por padrão nos consoles. Caso seja muito barulhento e você queira desligá-lo, você pode:

ActiveRecord::Base.logger = nil
gtd
fonte
Isso não está funcionando para mim em rails console. Isso funciona apenas para irb carregado diretamente do shell? (ou foi removido para os trilhos 3?)
Eric Hu,
2
Eles mudaram para um local mais sensível para o Rails 3 ... veja minha versão atualizada.
gtd
Existe alguma maneira de fazer isso automaticamente sempre que inicio o console? Algo como um gancho antes de carregar?
stream
1
@ stream7..Não sei se você precisa disso agora, mas você pode mover este código para environment.rb.. if "irb" == $0;ActiveRecord::Base.logger = Logger.new(STDOUT);end..com base nos comentários em http://weblog.jamisbuck.org/2007/1/8/watching-activerecord-do -é-coisa
rubyprince
Isso não responde à pergunta: como mostrar o sql para uma consulta específica durante a depuração sem executar a consulta.
bradw2k
87

Cole em puts query_object.classalgum lugar para ver com que tipo de objeto está trabalhando e, em seguida, consulte os documentos.

Por exemplo, no Rails 3.0, os escopos usam o ActiveRecord::Relationque tem um #to_sqlmétodo. Por exemplo:

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

Então, em algum lugar você pode fazer:

puts Contact.frequently_contacted.to_sql
pathdependent
fonte
3
Por que esta resposta não foi votada? Para Rails 3 é uma resposta muito melhor - você pode obter os resultados de qualquer instrução AR :: Relation apenas colocando ".to_sql" no final. Esta pergunta também fornece essa resposta: stackoverflow.com/questions/3814738/…
Steve Midgley
5
Cuidado: você não receberá todo o SQL se tiver um includesem seu parente
Barry Kelly
3
@BarryKelly Por que isso?
Vishnu Narang
25

Esta pode ser uma pergunta antiga, mas eu uso:

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

O explainmétodo fornecerá uma instrução SQL bastante detalhada sobre o que irá fazer

Akshat
fonte
3
Ele também executará a consulta, que pode não ser o que as pessoas desejam, apenas para registro.
Ibrahim
22

apenas use o to_sqlmétodo e ele irá gerar a consulta sql que será executada. funciona em uma relação de registro ativo.

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

ao fazer findisso, não funcionará, então você precisará adicionar esse id manualmente à consulta ou executá-lo usando where.

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

Isso é o que eu geralmente faço para obter o SQL gerado no console

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

Você tem que fazer isso ao iniciar o console pela primeira vez, se fizer isso depois de digitar algum código, não parece funcionar

Não consigo levar o crédito por isso, encontrei há muito tempo no blog de alguém e não consigo lembrar de quem é.

penger
fonte
1
Tenho certeza de que era o blog de Jamis Buck: weblog.jamisbuck.org/2007/1/8/…
rswolff
1
Não tenho certeza se é devido ao Rails 2.3 ou algo em meu ambiente, mas isso não funciona para mim. Veja minha resposta abaixo.
gtd.
Esta é uma ótima solução IMO.
Benjamin Atkin
10

Crie um arquivo .irbrc em seu diretório inicial e cole-o em:

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

Isso produzirá instruções SQL em sua sessão irb conforme você avança.

EDIT: Desculpe, ainda vou executar a consulta, mas é o mais próximo que eu conheço.

EDIT: Agora com arel, você pode construir escopos / métodos, desde que o objeto retorne ActiveRecord :: Relation e chame .to_sql nele e ele irá colocar o sql que vai ser executado.

nitecoder
fonte
Isso é o que tenho feito, mas estou mais interessado em simplesmente ver o SQL projetado que a consulta ActiveRecord irá gerar. Estou surpreso que não haja uma maneira simples de fazer isso ...
rswolff
5

Minha maneira típica de ver que sql ele usa é introduzir um "bug" no sql, então você receberá uma mensagem de erro enviada para o logger normal (e a tela da web) que tem o sql em questão. Não há necessidade de descobrir para onde o stdout está indo ...

rogerdpack
fonte
1
Solução espetacular, mas mais rápida :)
Ján Janočko
2

Experimente o plugin show_sql . O plugin permite que você imprima o SQL sem executá-lo

SampleModel.sql(:select => "DISTINCT(*)", :conditions => ["`date` > #{self.date}"], :limit => 1, :order => '`date`', :group => "`date`")
Harish Shetty
fonte
1
Parece que o link foi quebrado (erro 404). Provavelmente, Ryan Bigg excluiu o plugin.
DNNX
1

Você pode alterar o método de registro da conexão para gerar uma exceção, evitando que a consulta seja executada.

É um hack total, mas parece funcionar para mim (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
Além disso
fonte
0

No Rails 3, você pode adicionar esta linha ao config / environment / development.rb

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

No entanto, ele executará a consulta. Mas metade foi respondida:

Timbinous
fonte