如何在不实际执行的情况下获取ActiveRecord #ref创建的SQL语句?

我使用will_paginate与一些复杂的查询,它无法正确计算总记录数(为了显示正确数量的页面链接) – 即因为多列分组。

因此,我打算获取SELECT查询,该查询将用于检索所有记录,而不实际执行它,并使用SELECT COUNT(*) FROM ...手动包装它,以获取记录数。

有什么想法怎么做?

编辑:我正在使用Rails 2.3.x.

对于Rails 3:

在Rails 3文档中查看ActiveRecord :: Relation 文档 。

 # get the relation rel = User.complex_scope.chained_complex_scope # get the SQL # this does not execute the query sql = rel.to_sql # find out how many records # this executes the query behind the scenes count = rel.size 

看起来在Rails 2.x中,可以使用一个名为ActiveRecord::Base#construct_finder_sql的私有方法,我需要对它进行更多测试,看看它是否适用于我:

 ActionType.find(:all, :select => 'hosted, top_action_type, count(*) as count', :group => 'hosted, top_action_type').count #=> 6 sql = ActionType.send :construct_finder_sql, :select => 'hosted, top_action_type, count(*) as count', :group => 'hosted, top_action_type' #=> "SELECT hosted, top_action_type, count(*) as count FROM "action_types" GROUP BY hosted, top_action_type" ActionType.count_by_sql "SELECT COUNT(*) FROM (#{sql}) a" #=> 6 

我知道问题是“不执行它”,但#explain方法非常有用,至少应该在这里提到。 它对于调试慢速查询非常有用。

注意:它确实执行查询。

http://guides.rubyonrails.org/v3.2.8/active_record_querying.html#running-explain

 $ User.where("users.email LIKE '%longford%'").explain User Load (0.6ms) SELECT `users`.* FROM `users` WHERE (users.email LIKE '%longford%') => EXPLAIN for: SELECT `users`.* FROM `users` WHERE (users.email LIKE '%gmail%') +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | users | ALL | NULL | NULL | NULL | NULL | 5 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) 

不幸的是在Rails 2.x中实际上这很难。 我之前在Stack Overflow上发布了类似的问题,最后深入研究了Rails的源代码以找到方法。 它只是没有以允许这种方式的方式构建。

我最终做的是在我回滚的事务中运行查询,并且在事务的长度上将记录器设置为我自己可以读取的自己的StringIO对象。

这是来自内存,但希望你理解它足够调整它,如果它不起作用:

 Model.transaction do Model.logger = str = StringIO.new Model.complex_scope.chained_complex_scope Model.logger = ActiveRecord::Base.logger str.rewind str = str.read # perform some regex on str to get the actual query raise ActiveRecord::Rollback end 

它是丑陋的,我从来不喜欢它(我把它包装在一个sql { Model. complex_scope.chained_complex_scope } )但它有点对我有用(我只在开发中使用它,所以我有一些容忍错误)

对于Rails 4及以上版本,使用to_sql , 如上所述 。

原始答案

前段时间,我使用了一个名为sql_display的插件。

 >> Post.sql => "SELECT * FROM \"posts\"" >> Post.sql(:order => "id DESC") => "SELECT * FROM \"posts\" ORDER id DESC" >> Post.scoped({}).sql => "SELECT * FROM \"posts\"" >> Post.count_sql => "SELECT count(*) AS count_all FROM \"posts\""