【问题标题】:How can I see the SQL that will be generated by a given ActiveRecord query in Ruby on Rails如何在 Ruby on Rails 中查看给定 ActiveRecord 查询将生成的 SQL
【发布时间】: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,但不一定执行该查询。

【问题讨论】:

    标签: sql ruby-on-rails activerecord


    【解决方案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 语句的结果。这个问题也提供了这个答案:stackoverflow.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"
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-12-16
          • 1970-01-01
          • 1970-01-01
          • 2012-10-05
          • 1970-01-01
          • 2012-05-10
          相关资源
          最近更新 更多