web-dev-qa-db-ja.com

実際に実行せずにActiveRecord#findによって作成されたSQLステートメントを取得するにはどうすればよいですか?

いくつかの複雑なクエリで_will_paginate_を使用していますが、(適切な数のページリンクを表示するために)合計レコード数を正しく計算できません。つまり、複数の列でグループ化されているためです。

したがって、実際に実行せずにすべてのレコードを取得するために使用するSELECTクエリを取得し、レコード数を取得するためにSELECT COUNT(*) FROM ...で手動でラップするつもりです。

それを行う方法についてのアイデアはありますか?

編集:私はRails 2.3.xを使用しています

26

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
42
yfeldblum

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
10

「実行せずに」という質問を知っていますが、#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)
2
daino3

残念ながら、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 }で囲みました)が、それは私にとってはうまくいきました(私は開発でのみ使用したので、エラーに対してある程度の許容度がありました)

0
Bo Jeanes

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\""
0
Harish Shetty