Как я могу получить инструкцию SQL, созданную ActiveRecord # find, фактически не выполнив ее?
Я использую will_paginate
с некоторыми сложными запросами и не могу правильно вычислить количество полных записей (чтобы отобразить правильное количество ссылок на страницы) - а именно из-за группировки по нескольким столбцам.
Итак, я собираюсь получить запрос SELECT, который будет использоваться для извлечения ВСЕХ записей, без его фактического выполнения, и обернуть его с помощью SELECT COUNT(*) FROM ...
вручную, чтобы получить количество записей.
Любые идеи, как это сделать?
Изменить: я использую Rails 2.3.x
Ответы
Ответ 1
Для Rails 3:
Просмотрите документы ActiveRecord:: Relation в 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
Ответ 2
Кажется, что в 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
Ответ 3
Когда-то я использовал плагин под названием 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
К сожалению, в Rails 2.x это довольно сложно. Я отправил аналогичный вопрос в Qaru раньше и в конечном итоге углубился в исходный код 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 }
), но он работал у меня (я использовал его только в разработке, поэтому у меня был некоторый толерант к ошибкам)
Ответ 5
Я знаю, что вопрос задает "без его выполнения", но метод #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)