Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get SQL statement created by ActiveRecord#find without actually executing it?

I am using will_paginate with some complicated queries and it is unable to correctly calculate number of total records (in order to display proper number of page links) - namely because of grouping by multiple columns.

So, I am intending to get the SELECT query which would be used to retrieve ALL records, without actually executing it, and wrap it with SELECT COUNT(*) FROM ... manually, in order to get the number of records.

Any ideas how to do it?

Edit: I am using Rails 2.3.x

like image 386
Mladen Jablanović Avatar asked Sep 28 '10 16:09

Mladen Jablanović


People also ask

What is Arel SQL?

Arel is a SQL abstraction that ActiveRecord uses to build SQL queries. Arel wraps each component of the SQL query language with Ruby objects and provides an expressive DSL for composing SQL queries. When using Arel, you're mainly interacting with tables ( Arel::Table ) and nodes ( Arel::Nodes::Node subclasses).

What is ActiveRecord relation?

The Relation Class. Having queries return an ActiveRecord::Relation object allows us to chain queries together and this Relation class is at the heart of the new query syntax. Let's take a look at this class by searching through the ActiveRecord source code for a file called relation.

What is ActiveRecord in Ruby on Rails?

Active Record is the M in MVC - the model - which is the layer of the system responsible for representing business data and logic. Active Record facilitates the creation and use of business objects whose data requires persistent storage to a database.


3 Answers

For Rails 3:

Check out the ActiveRecord::Relation docs at the Rails 3 docs.

# 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 
like image 154
yfeldblum Avatar answered Oct 11 '22 15:10

yfeldblum


It seems thatm in Rails 2.x, a private method called ActiveRecord::Base#construct_finder_sql could be used, I need to test it more and see whether it will work for me:

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
like image 22
Mladen Jablanović Avatar answered Oct 11 '22 16:10

Mladen Jablanović


I know the question asks "without executing it", but the #explain method is super useful and should at least be mentioned here. It is extremely useful for debugging slow queries.

Note: It does execute the query though.

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)
like image 22
daino3 Avatar answered Oct 11 '22 15:10

daino3