如何查看Ruby on Rails中给定ActiveRecord查询将生成的SQL


116

我想查看给定ActiveRecord查询将生成的SQL语句。我知道可以在发出查询后从日志中获取此信息,但是我想知道是否存在可以调用和ActiveRecord Query的方法。

例如:

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

我想打开irb控制台,最后添加一个方法,该方法将显示该查询将生成的SQL,但不一定执行该查询。

Answers:


12

我上一次尝试执行此操作时,还没有官方方法可以执行此操作。我求助于使用函数find及其朋友直接用于生成查询的函数。它是私有API,因此Rails 3可能会完全破坏它,但是对于调试来说,这是一个好的解决方案。

您必须使用construct_finder_sql(options)lib/active_record/base.rb:1681)方法,send因为它是私有的。

编辑construct_finder_sql 在Rails 5.1.0.beta1中已删除


1
这接近我的想法。我猜一个人可以写一个可以做类似以下操作的插件:SampleModel.find(:all,:select =>“ DISTINCT(*)” date,:conditions => [“ >#{self.date}”],:limit => 1,:order =>' date' date,:group =>“ ”).show_genic_sql并调用此方法的Construct_finder_sql方法。
rswolff

1
使用DataMapper可以,因为它不会立即运行查询。另一方面,ActiveRecord立即执行查询。 show_generated_sql将作用于已从中检索到的数据集find
约翰·米勒

4
在Rails 3 construct_finder_sql中确实已删除
Veger 2011年

190

与penger相似,但是即使在加载了类并且记录器已缓存之后,控制台也可以在控制台中随时运行:

对于Rails 2:

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

对于Rails 3.0.x:

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

对于Rails> = 3.1.0,默认情况下已在控制台中完成此操作。如果它太嘈杂,并且您想要将其关闭,可以执行以下操作:

ActiveRecord::Base.logger = nil

这不适用于rails console。它仅适用于直接从外壳加载的irb吗?(或者是否已删除3条导轨?)
Eric Hu

2
他们将其移到了更适合Rails 3的地方...请参阅我的更新版本。
gtd

每次启动控制台时,有什么方法可以自动执行此操作吗?像一个事前挂钩?
stream7 2011年

1
@ stream7 ..我不知道您现在是否需要它,但是您可以将代码移到environment.rb.... if "irb" == $0;ActiveRecord::Base.logger = Logger.new(STDOUT);endhttp://weblog.jamisbuck.org/2007/1/8/watching-activerecord-do中的
rubyprince

这不能回答问题:如何在调试时不运行查询的情况下显示特定查询的sql。
bradw2k

87

坚持一个puts query_object.class地方,看看有什么类型的对象与你的工作,然后查找的文档。

例如,在Rails 3.0中,作用域使用ActiveRecord::Relation具有#to_sql方法的方法。例如:

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

然后,您可以在某处执行以下操作:

puts Contact.frequently_contacted.to_sql

3
为什么不赞成这个答案?对于Rails 3,这是一个更好的答案-您只需在结尾处添加“ .to_sql”,即可获得任何AR :: Relation语句的结果。这个问题也提供了答案:stackoverflow.com/questions/3814738/…
史蒂夫·米德利

5
当心:如果您拥有一个 includes的关系中关系,
Barry Kelly

3
@BarryKelly为什么?
Vishnu Narang

25

这可能是一个老问题,但我使用:

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

explain方法将给出有关其功能的相当详细的SQL语句


3
它也将运行查询,这可能不是人们想要的,仅用于记录。
Ibrahim

22

只需使用to_sqlmethod,它将输出将要运行的sql查询。它适用于活动记录关系。

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

这样做时find,它将无法正常工作,因此您需要将该ID手动添加到查询中,或使用where运行它。

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

11

我通常这样做是为了在控制台中生成SQL

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

首次启动控制台时必须执行此操作,如果在键入一些代码后执行此操作,则它似乎不起作用

无法真正相信这一点,是很久以前从某人的博客中找到它的,也不记得是谁了。


1
我很确定这是Jamis Buck的博客:weblog.jamisbuck.org/2007/1/8/…– rswolff
2009年

1
我不确定这是由于Rails 2.3还是我的环境中的某些原因引起的,但这对我不起作用。请在下面查看我的回复。
gtd

这是IMO的绝佳解决方案。
本杰明·阿特金

10

在您的主目录中创建一个.irbrc文件,并将其粘贴到:

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

这将在您执行操作时将SQL语句输出到您的irb会话中。

编辑:对不起,仍将执行查询,但它是我所知道的最接近的。

编辑:现在有了arel,只要对象返回ActiveRecord :: Relation并对其调用.to_sql,就可以建立作用域/方法,它将输出将要执行的sql。


这是我一直在做的事情,但是我更感兴趣的是仅看到ActiveRecord查询将生成的计划SQL。我很惊讶没有简单的方法可以做到这一点
rswolff

5

我查看其使用的sql的典型方法是在sql中引入“错误”,然后您会收到一条错误消息,将其发送到具有该sql的普通记录器(和Web屏幕)。无需找到stdout的去向...


1
壮观而迅速的解决办法:)
扬Janočko

2

尝试使用show_sql插件。该插件使您无需运行即可打印SQL

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

1
看起来该链接已损坏(404错误)。可能是Ryan Bigg删除了该插件。
DNNX 2012年

1

您可以更改连接的日志方法以引发异常,从而阻止运行查询。

这是一个完全的hack,但是它似乎对我有用(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

在Rails 3中,您可以将此行添加到config / environments / development.rb

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

但是它将执行查询。但是一半得到了回答:

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.