2010-09-28 10 views
22

Estoy utilizando will_paginate con algunas consultas complicadas y no se puede calcular correctamente el número total de registros (para mostrar el número correcto de enlaces de página), es decir, debido a la agrupación por varias columnas.¿Cómo puedo obtener una declaración SQL creada por ActiveRecord # find sin realmente ejecutarla?

Por lo tanto, tengo la intención de obtener la consulta SELECT que se utilizará para recuperar TODOS los registros, sin ejecutarlo realmente, y envolverlo con SELECT COUNT(*) FROM ... manualmente, para obtener el número de registros.

¿Alguna idea de cómo hacerlo?

Editar: Estoy usando Rails 2.3.x

+0

posible duplicado de [Obtener el SQL que se ejecuta desde un determinado método o named_scope] (http://stackoverflow.com/questions/974020/get-the-sql-that- would-be-be-be-from-a-certain-method-or-named-scope) –

Respuesta

34

para los carriles 3:

Mira la documentación ActiveRecord :: relaciones en el Rails 3 docs. Hace

# 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

Gracias, lo tendré en cuenta cuando migremos a 3. Olvidé mencionar que todavía estamos usando 2.3.x, lo siento. –

+0

También funciona para Rails 4 :) –

0

En algún momento, he usado un plugin llamado sql_display para esto.

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

Gracias, lo intentaré. ¿Sabes quizás si construye SQL imitando el comportamiento de RA, o envuelve AR de alguna manera? Es decir. ¿Puedo confiar en que dará exactamente la misma consulta que 'AR # find'? –

0

Desafortunadamente en Rails 2.x esto es realmente bastante difícil. He publicado una pregunta similar sobre Stack Overflow antes y terminé cavando profundamente en el código fuente de Rails para encontrar una manera. Simplemente no está diseñado de una manera que permita esto.

Lo que terminé haciendo fue ejecutar la consulta en una transacción que hice retroceder, y durante la transacción estableciendo el registrador en mi propio objeto StringIO que pude leer después.

Se trata de la memoria, pero esperamos que pueda entender lo suficiente como para ajustarlo si no funciona:

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 

Es feo como el infierno y nunca me ha gustado mucho (me envolvió en una sql { Model. complex_scope.chained_complex_scope }), pero funcionó un poco para mí (solo lo usé en desarrollo, así que tuve cierta tolerancia para los errores)

9

Parece que en Rails 2.x, se puede usar un método privado llamado ActiveRecord::Base#construct_finder_sql, necesito probarlo más y ver si funcionará para mí:

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

Probado y realmente funciona bien. Supongo que debería aceptar mi propia respuesta.:/ –

+0

¿Hay alguna manera de agregar *: incluir * también? –

0

Sé que la pregunta es "sin ejecutarlo", pero el método #explain es muy útil y al menos debería mencionarse aquí. Es extremadamente útil para la depuración de consultas lentas.

Nota: Sí ejecuta la consulta.

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) 
Cuestiones relacionadas