Как я могу получить оператор SQL, созданный ActiveRecord # find, без его фактического выполнения? - PullRequest
25 голосов
/ 28 сентября 2010

Я использую will_paginate с некоторыми сложными запросами и не могу правильно рассчитать количество общих записей (для отображения правильного количества ссылок на страницы), а именно из-за группировки по нескольким столбцам.

Итак, я собираюсь получить запрос SELECT, который будет использоваться для извлечения ВСЕХ записей без фактического их выполнения, и обернуть его с помощью SELECT COUNT(*) FROM ... вручную, чтобы получить количество записей.

Любые идеикак это сделать?

Редактировать: я использую Rails 2.3.x

Ответы [ 5 ]

42 голосов
/ 28 сентября 2010

Для Rails 3:

Ознакомьтесь с документацией ActiveRecord :: Relation на Rails 3 документации .

# 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
10 голосов
/ 29 сентября 2010

Похоже, что в 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
1 голос
/ 02 февраля 2016

Я знаю, что вопрос задается «без его выполнения», но метод #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)
0 голосов
/ 29 сентября 2010

К сожалению, в Rails 2.x это на самом деле довольно сложно. Я уже публиковал аналогичный вопрос о переполнении стека и в конце концов углубился в исходный код 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 голосов
/ 28 сентября 2010

Для 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\""
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...