Comment puis-je voir le SQL qui sera généré par une requête ActiveRecord donnée dans Ruby on Rails


116

Je voudrais voir l'instruction SQL qu'une requête ActiveRecord donnée générera. Je reconnais que je peux obtenir ces informations à partir du journal une fois la requête émise, mais je me demande s'il existe une méthode qui peut être appelée et ActiveRecord Query.

Par exemple:

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

Je voudrais ouvrir la console irb et appliquer une méthode à la fin qui montrerait au SQL que cette requête générera, mais n'exécutera pas nécessairement la requête.

Réponses:


12

La dernière fois que j'ai essayé de le faire, il n'y avait aucun moyen officiel de le faire. J'ai eu recours à la fonction que findses amis et ses amis utilisent pour générer directement leurs requêtes. C'est une API privée, donc il y a un risque énorme que Rails 3 la casse totalement, mais pour le débogage, c'est une solution correcte.

La méthode est construct_finder_sql(options)( lib/active_record/base.rb:1681) que vous devrez utiliser sendcar elle est privée.

Edit : a construct_finder_sql été supprimé dans Rails 5.1.0.beta1 .


1
C'est proche de ce que je pense. Je suppose qu'une personne pourrait écrire un plugin qui ferait quelque chose comme: SampleModel.find (: all,: select => "DISTINCT (*)" date,: conditions => [" > # {self.date}"],: limit => 1 ,: order => ' date',: group => " date") .show_generated_sql et faites appeler la méthode construct_finder_sql.
rswolff

1
Avec DataMapper, vous pouvez le faire car il n'exécute pas la requête tout de suite. ActiveRecord, d'autre part, exécute la requête immédiatement. show_generated_sqlagira sur un ensemble de données déjà récupéré find.
John F. Miller

4
In Rails 3 construct_finder_sqlest en effet supprimé
Veger

190

Similaire à penger, mais fonctionne à tout moment dans la console même après le chargement des classes et la mise en cache de l'enregistreur:

Pour les rails 2:

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

Pour les rails 3.0.x:

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

Pour Rails> = 3.1.0, cela est déjà fait par défaut dans les consoles. Dans le cas où il est trop bruyant et que vous souhaitez l' éteindre, vous pouvez faire:

ActiveRecord::Base.logger = nil

Cela ne fonctionne pas pour moi rails console. Cela fonctionne-t-il uniquement pour irb chargé directement depuis le shell? (ou a-t-il été retiré pour les rails 3?)
Eric Hu

2
Ils l'ont déplacé vers un endroit plus raisonnable pour Rails 3 ... voir ma version mise à jour.
gtd

Existe-t-il un moyen de le faire automatiquement à chaque fois que je démarre la console? Quelque chose comme un crochet avant de charger?
stream7

1
@ stream7..Je ne sais pas si vous en avez besoin maintenant, mais vous pouvez déplacer ce code vers environment.rb.. .. if "irb" == $0;ActiveRecord::Base.logger = Logger.new(STDOUT);endobtenu ceci à partir des commentaires dans http://weblog.jamisbuck.org/2007/1/8/watching-activerecord-do -it-s-thing
rubyprince

Cela ne répond pas à la question: comment afficher le SQL pour une requête particulière lors du débogage sans exécuter la requête.
bradw2k

87

Collez un puts query_object.classquelque part pour voir avec quel type d'objet vous travaillez, puis recherchez les documents.

Par exemple, dans Rails 3.0, les étendues utilisent ActiveRecord::Relationqui a une #to_sqlméthode. Par exemple:

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

Ensuite, quelque part vous pouvez faire:

puts Contact.frequently_contacted.to_sql

3
Pourquoi cette réponse n'a-t-elle pas été votée? Pour Rails 3, c'est une bien meilleure réponse - vous pouvez obtenir les résultats de n'importe quelle instruction AR :: Relation simplement en mettant ".to_sql" à la fin. Cette question fournit également cette réponse: stackoverflow.com/questions/3814738/…
Steve Midgley

5
Attention: vous n'obtiendrez pas tout le SQL si vous en avez un includesdans votre relation
Barry Kelly

3
@BarryKelly Pourquoi est-ce?
Vishnu Narang

25

C'est peut-être une vieille question mais j'utilise:

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

La explainméthode donnera une instruction SQL assez détaillée sur ce qu'elle va faire


3
Il exécutera également la requête, ce qui n'est peut-être pas ce que les gens veulent, juste pour mémoire.
Ibrahim

22

utilisez simplement la to_sqlméthode et il affichera la requête SQL qui sera exécutée. cela fonctionne sur une relation d'enregistrement active.

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

lorsque vous le faites find, cela ne fonctionnera pas, vous devrez donc ajouter cet identifiant manuellement à la requête ou l'exécuter en utilisant where.

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

11

C'est ce que je fais habituellement pour générer SQL dans la console

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

Vous devez le faire lorsque vous démarrez la console pour la première fois, si vous faites cela après avoir tapé du code, cela ne semble pas fonctionner

Je ne peux pas vraiment m'en attribuer le mérite, je l'ai trouvé il y a longtemps sur le blog de quelqu'un et je ne me souviens plus à qui il appartient.


1
Je suis presque sûr que c'était le blog de Jamis Buck: weblog.jamisbuck.org/2007/1/8/…
rswolff

1
Je ne sais pas si cela est dû à Rails 2.3 ou à quelque chose dans mon environnement, mais cela ne fonctionne pas pour moi. Voir ma réponse ci-dessous.
gtd

C'est une excellente solution IMO.
Benjamin Atkin

10

Créez un fichier .irbrc dans votre répertoire personnel et collez-le dans:

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

Cela produira des instructions SQL dans votre session irb au fur et à mesure.

EDIT: Désolé, cela exécutera toujours la requête, mais c'est le plus proche que je connaisse.

EDIT: Maintenant avec arel, vous pouvez créer des portées / méthodes tant que l'objet retourne ActiveRecord :: Relation et appelle .to_sql dessus et il mettra le sql qui va être exécuté.


C'est ce que j'ai fait, mais je suis plus intéressé par simplement voir le SQL projeté que la requête ActiveRecord générera. Je suis surpris qu'il n'y ait pas de moyen simple de le faire ...
rswolff

5

Ma façon typique de voir quel sql il utilise est d'introduire un "bogue" dans le sql, puis vous obtiendrez un message d'erreur envoyé à l'enregistreur normal (et à l'écran Web) qui a le sql en question. Pas besoin de savoir où va stdout ...


1
Solution spectaculaire mais rapide :)
Ján Janočko

2

Essayez le plugin show_sql . Le plugin vous permet d'imprimer le SQL sans l'exécuter

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

1
On dirait que le lien s'est rompu (erreur 404). Probablement, Ryan Bigg a supprimé le plugin.
DNNX

1

Vous pouvez modifier la méthode du journal de la connexion pour déclencher une exception, empêchant l'exécution de la requête.

C'est un hack total, mais cela semble fonctionner pour moi (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

Dans Rails 3, vous pouvez ajouter cette ligne au fichier config / environnements / development.rb

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

Il exécutera cependant la requête. Mais la moitié a répondu:

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.