如何在 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_sql
was 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 参数数量错误(给定 0 预期 1)