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

Posted

技术标签:

【中文标题】如何在 Ruby on Rails 中查看给定 ActiveRecord 查询将生成的 SQL【英文标题】:How can I see the SQL that will be generated by a given ActiveRecord query in Ruby on Rails 【发布时间】:2010-11-23 13:46:31 【问题描述】:

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

例如:

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

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

【问题讨论】:

【参考方案1】:

在您的主目录中创建一个 .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。我很惊讶没有简单的方法可以做到这一点......【参考方案2】:

当我上次尝试这样做时,没有官方方法可以做到这一点。我求助于使用find 及其朋友直接生成查询的函数。它是私有 API,因此 Rails 3 完全破坏它的风险很大,但对于调试来说,这是一个不错的解决方案。

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

编辑construct_finder_sqlwas removed in Rails 5.1.0.beta1。

【讨论】:

这与我的想法很接近。我想一个人可以编写一个插件来执行以下操作: SampleModel.find(:all, :select => "DISTINCT(*)", :conditions => ["date > #self.date"] , :limit => 1, :order => 'date', :group => "date").show_generated_sql 并调用construct_finder_sql方法。 使用 DataMapper 可以,因为它不会立即运行查询。另一方面,ActiveRecord 立即执行查询。 show_generated_sql 将作用于已从 find 检索到的数据集。 在 Rails 3 中 construct_finder_sql 确实被删除了【参考方案3】:

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

这完全是 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

【讨论】:

【参考方案4】:

这是我通常在控制台中生成 SQL 的方法

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

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

这不能真正归功于此,很久以前从某人的博客中找到的,不记得是谁了。

【讨论】:

我很确定这是 Jamis Buck 的博客:weblog.jamisbuck.org/2007/1/8/… 我不确定这是由于 Rails 2.3 还是我的环境中的某些原因,但这对我不起作用。请参阅下面的回复。 这是 IMO 的绝佳解决方案。【参考方案5】:

与 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 吗? (或者是否为 rails 3 移除了它?) 他们将它移到了更适合 Rails 3 的位置...查看我的更新版本。 每次我启动控制台时有没有办法自动执行此操作?类似于前负载挂钩的东西? @stream7..我不知道你现在是否需要这个,但你可以把这个代码移到environment.rb..if "irb" == $0;ActiveRecord::Base.logger = Logger.new(STDOUT);end..从http://weblog.jamisbuck.org/2007/1/8/watching-activerecord-do-it-s-thing的cmets那里得到这个 这没有回答问题:如何在不运行查询的情况下调试时显示特定查询的 sql。【参考方案6】:

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

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

【讨论】:

看起来链接已损坏(404 错误)。可能,Ryan Bigg 删除了插件。【参考方案7】:

在某处粘贴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

【讨论】:

为什么这个答案没有被赞成?对于 Rails 3,这是一个更好的答案 - 您只需在末尾添加“.to_sql”即可获得任何 AR::Relation 语句的结果。这个问题也提供了这个答案:***.com/questions/3814738/… 当心:如果您的关系中有 includes,您将无法获得所有 SQL @BarryKelly 为什么会这样? @VishnuNarang 我相信这是因为includes 需要多个查询。【参考方案8】:

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

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

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

【讨论】:

【参考方案9】:

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

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

explain 方法会给出一个相当详细的 SQL 语句来说明它要做什么

【讨论】:

它还会运行查询,这可能不是人们想要的,只是为了记录。【参考方案10】:

我查看它使用什么 sql 的典型方法是在 sql 中引入一个“错误”,然后您会收到一条错误消息吐出到有问题的 sql 的普通记录器(和 Web 屏幕)。无需查找 stdout 的去向...

【讨论】:

壮观但最快的解决方案:)【参考方案11】:

只需使用to_sql 方法,它就会输出将要运行的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"

【讨论】:

以上是关于如何在 Ruby on Rails 中查看给定 ActiveRecord 查询将生成的 SQL的主要内容,如果未能解决你的问题,请参考以下文章

ruby on rails如何安装

如何在 ruby​​ on rails 中更改路线?

作物哈希结构:Ruby on rails

Ruby on Rails 参数数量错误(给定 0 预期 1)

如何在 Ruby on Rails 中通过关联订购 has_many?

ruby on rails 网页上如何实时显示服务器网速?