2009-06-17 6 views
7

La aplicación My Rails está empezando a necesitar consultas complicadas. ¿Debo comenzar a usar consultas SQL sin formato? ¿Cuál es la tendencia en la comunidad de Rails?Las consultas de My Rails se están empezando a complicar, ¿debería cambiar a consultas SQL sin formato? ¿Qué haces?

Actualización: consultas

mi no me ha escrito en este momento, yo quería hacer esta pregunta antes de empezar. Pero aquí hay un ejemplo de lo que quiero hacer:

Tengo libros que tienen categorías. Quiero decir-

Give me all books that were: 
-created_at (added to store) between date1 and date2 
-updated_at before date3 
-joined with books that exist in shopping carts right now 

no he escrito la consulta todavía, pero creo que la versión de los carriles será algo como esto:

books_to_consider = Book.find(:all, 
         :conditions => "created_at <= '#{date2}' AND created_at >= '#{date1}' AND updated_at <= '#{date3}'", 
         :joins => "as b inner join carts as c on c.book_id = b.id") 

No estoy diciendo ActiveRecord no puede manejar esta consulta , pero ¿es más aceptado ir con SQL sin procesar para la legibilidad (o tal vez hay otras limitaciones que aún no conozco)?

+0

Podría dar un ejemplo concreto? –

+0

¿Puedes dar algunos ejemplos de tus consultas que crees que son demasiado complicadas para usar las funciones de Rails/ActiveRecord? – cpjolicoeur

+2

Tenga en cuenta que ese tipo de interpolación es una muy mala idea. ¿Qué sucede si date1, date2 o date3 contienen un carácter "'"? Estás en un lenguaje dinámico; esos podrían ser cuerdas. –

Respuesta

13

La idea general es atenerse a las consultas generadas ActiveRecord tanto como sea posible, y usar fragmentos de SQL solo cuando sea necesario. Los fragmentos de SQL se admiten explícitamente porque los creadores de ActiveRecord se dieron cuenta de que SQL no se puede abstraer por completo.

El uso del método find sin fragmentos SQL generalmente se recompensa con un mejor mantenimiento. Dada su ejemplo, pruebe:

Book.find(:all, 
    :conditions => ["created_at >= ? AND created_at <= ? AND updated_at <= ?", 
        date1, date2, date3] 
    :include => :carts) 

El :inlude => :carts va a hacer la unión si se ha añadido a su modelo has_many :cartsBook. Como puede ver, no tiene que haber mucho SQL involucrado. Incluso las citas y el escape de la entrada se pueden dejar en Rails, mientras se siguen utilizando los literales SQL para manejar los operadores >= y <=.

Yendo un poco más lejos, puede que sea aún más clara:

class Book < AciveRecord::Base 
    # Somewhere in your Book model: 
    named_scope :created_between, lambda { |start_date, end_date| 
    { :conditions => { :created_at => start_date..end_date } } 
    } 
    named_scope :updated_before, lambda { |date| 
    { :conditions => ["updated_at <= ?", date] } 
    } 
    # ... 
end 

Book.created_between(date1, date2).updated_before(date3).find(:all, 
    :include => :carts) 

Actualización: el punto de la named_scope s es, por supuesto, a la reutilización las condiciones. Depende de usted decidir si tiene sentido o no poner un conjunto de condiciones en un ámbito determinado o no.

+0

Muy buenas cosas. Aunque, ¿qué pasa si quiero todos los libros, excepto los que existen en los carritos con delivery_date = hoy? Creo que quiero usar el comando intersectar pero tengo problemas para encontrar documentación para esto. cualquier ayuda o enlace sería grandioso – Tony

+1

Creo que ES el momento en que dirías ": condición => cosas" –

+0

Y/o ve por esa dulce y dulce declaración de named_scope: deliver_today, lambda {: conditions => [" delivery_date <=? ", hoy]} O tal vez no sea bueno. Esa segunda opción es realmente probable, pero aún así lo invito a probar: P –

3

Como molf dice con: incluir, .find() tiene la ventaja de la carga ansiosa de los niños. Además, hay varios complementos, como la paginación, que ajustarán la función de búsqueda. Deberá usar .find() para usar los complementos.

Si tiene una consulta SQL realmente compleja, recuerde que .find() usa su cadena de parámetros exacta. Siempre se puede inyectar su propio código SQL:

: condiciones => [ "Identificación de unión (seleccionar * de la tabla ...

Y no se olvide que hay una gran cantidad de parámetros opcionales para .encontrar()

  • : condiciones - un fragmento de SQL como "administrador = 1", [ "nombre_usuario =?", nombre de usuario], o [ "nombre_usuario =: nombre_usuario", {: nombre_usuario => nombre_usuario}]. Ver condiciones en la introducción.
  • : orden - Un fragmento de SQL como "created_at DESC, name".
  • : group - Nombre de atributo por el cual se debe agrupar el resultado. Utiliza la cláusula GROUP BY SQL.
  • : having - Combinado con +: group + esto se puede utilizar para filtrar los registros que devuelve GROUP BY. Utiliza la cláusula HAVING SQL.
  • : límite: un entero que determina el límite en el número de filas que se deben devolver.
  • : desplazamiento: un entero que determina el desplazamiento desde donde se deben buscar las filas. Por lo tanto, a 5, omitiría las filas 0 a 4.
  • : uniones - Ya sea un fragmento SQL para uniones adicionales como "IZQUIERDA UNIR comentarios en comentarios.post_id = id" (rara vez es necesario), asociaciones nombradas en la misma forma que se utiliza para la opción: include, que realizará una UNIÓN INTERNA en la (s) tabla (s) asociada (s), o una matriz que contiene una mezcla de ambas cadenas y asociaciones nombradas. Si el valor es una cadena, los registros serán devueltos de solo lectura ya que tendrán atributos que no se corresponden con las columnas de la tabla. Pase: readonly => false para anular.
  • : incluir - Asociaciones de nombres que se deben cargar al costado. Los símbolos nombrados se refieren a asociaciones ya definidas. Ver carga ansiosa en Asociaciones.
  • : seleccionar - De forma predeterminada, esto es "*" como en "SELECCIONAR * DESDE", pero se puede cambiar si, por ejemplo, desea hacer una unión pero no incluir las columnas unidas. Toma una cadena con el fragmento SELECT SQL (por ejemplo, "id, name").
  • : from - Por defecto, este es el nombre de la tabla de la clase, pero se puede cambiar a un nombre de tabla alternativo (o incluso al nombre de una vista de base de datos).
  • : solo lectura: marque los registros devueltos como de solo lectura para que no se puedan guardar ni actualizar.
  • : lock - Un fragmento de SQL como "FOR UPDATE" o "LOCK IN SHARE MODE". : lock => true proporciona el bloqueo exclusivo predeterminado de la conexión, generalmente "FOR UPDATE".

src: http://api.rubyonrails.org/classes/ActiveRecord/Base.html#M002553

+0

Alguien edita esto para que los parámetros sean más legibles. –

Cuestiones relacionadas