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

时间:2010-09-28 16:04:57

标签: ruby-on-rails activerecord count

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

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

任何想法怎么做?

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

5 个答案:

答案 0 :(得分:42)

对于Rails 3:

查看Rails 3 docs上的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

答案 1 :(得分:10)

似乎在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

答案 2 :(得分:1)

我知道这个问题并没有执行#34;但是#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)

答案 3 :(得分:0)

对于Rails 4及更高版本,请使用above中解释的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\""

答案 4 :(得分:0)

不幸的是在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 }中)但它对我有用(我只在开发中使用它,所以我对错误有一些容忍度)