2009-06-17 11 views
7

L'app My Rails sta iniziando a richiedere query complicate. Dovrei semplicemente iniziare a utilizzare query SQL raw? Qual è la tendenza nella community di Rails?Le query di My Rails iniziano a complicarsi, dovrei passare alle query SQL raw? cosa fai?

Aggiornamento: query

io non ho scritto in questo momento, ho voluto porre questa domanda prima di iniziare. Ma ecco un esempio di ciò che voglio fare:

Ho libri che hanno categorie. Voglio dire-

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 

non ho ancora scritto la query, ma penso che la versione rotaie sarà qualcosa di simile:

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

Non sto dicendo ActiveRecord non può gestire questa query , ma è più accettabile andare con raw SQL per la leggibilità (o forse ci sono altre limitazioni che non conosco ancora)?

+0

Potrebbe dare un esempio concreto? –

+0

puoi fornire alcuni esempi delle tue domande che ritieni siano troppo complicate per l'utilizzo delle funzionalità di Rails/ActiveRecord? – cpjolicoeur

+2

Si prega di notare che quel tipo di interpolazione è una pessima idea. Cosa succede se date1, date2 o date3 contengono un carattere ""? Sei in un linguaggio dinamico; quelli potrebbero essere stringhe. –

risposta

13

L'idea generale è di attenersi il più possibile a ActiveRecord query generati e utilizzare i frammenti SQL solo se necessario. I frammenti SQL sono esplicitamente supportati perché i creatori di ActiveRecord hanno realizzato che SQL non può essere completamente estratto.

L'utilizzo del metodo find senza frammenti SQL è generalmente premiato con una migliore manutenzione. Dato il tuo esempio, provate:

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

Il :inlude => :carts farà il join se hai aggiunto has_many :carts al modello Book. Come puoi vedere, non è necessario coinvolgere molto SQL. Anche la citazione e la fuga di input possono essere lasciate a Rails, mentre si usano ancora letterali SQL per gestire gli operatori >= e <=.

Andando un po 'più avanti, si può rendere ancora più chiaro:

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) 

Aggiornamento: il punto della named_scope s è, naturalmente, per il riutilizzo le condizioni. Sta a te decidere se ha senso mettere un insieme di condizioni in un ambito con o senza nome.

+0

Roba molto buona. Anche se cosa voglio se tutti i libri eccetto quelli esistenti nei carrelli con delivery_date = oggi? Penso di voler usare il comando intersect, ma ho difficoltà a trovare la documentazione per questo. qualsiasi aiuto o link sarebbe fantastico – Tony

+1

Penso che sia il momento in cui diresti ": condition => stuff" –

+0

E/o vai per quella dolce, dolce roba dichiarativa di named_scope: delivering_today, lambda {: conditions => [" delivery_date <=? ", oggi]} Oppure potrei non essere bravo. Questa seconda opzione è molto probabile, ma ti invito comunque a provare: P –

3

Come il matto sta dicendo con: include, .find() ha il vantaggio del carico ansioso dei bambini. Inoltre, ci sono diversi plugin, come l'impaginazione, che avvolgeranno la funzione di ricerca. Dovrai usare .find() per usare i plugin.

Se si dispone di una query sql davvero complessa, ricordare che .find() utilizza la stringa di parametri esatta. È sempre possibile iniettare il proprio codice SQL:

: condizioni => [ "id in unione (select * from tabella ...

E non dimenticare ci sono un sacco di parametri opzionali per.trovare()

  • : condizioni - un frammento di SQL come "amministratore = 1", [ "nome_utente =?", il nome utente], o [ "nome_utente =: USER_NAME", {: user_name => user_name}]. Vedi le condizioni nell'introduzione.
  • : order - Un frammento SQL come "created_at DESC, name".
  • : group - Un nome di attributo con il quale il risultato deve essere raggruppato. Utilizza la clausola GROUP BY SQL.
  • : con - Combinato con +: gruppo + questo può essere utilizzato per filtrare i record restituiti da GROUP BY. Utilizza la clausola SQL HAVING.
  • : limit - Un numero intero che determina il limite sul numero di righe che devono essere restituite.
  • : offset - Un numero intero che determina l'offset da cui devono essere recuperate le righe. Quindi, a 5, salta le righe da 0 a 4.
  • : join - Un frammento SQL per join aggiuntivi come "LEFT JOIN comments ON comments.post_id = id" (raramente necessario), associazioni denominate nella stessa forma utilizzata per l'opzione: include, che eseguirà un JOIN INNER sulle tabelle associate o una matrice contenente una combinazione di stringhe e associazioni denominate. Se il valore è una stringa, i record verranno restituiti in sola lettura poiché avranno attributi che non corrispondono alle colonne della tabella. Passa: readonly => false per sovrascrivere.
  • : include - Associa associazioni di nomi che devono essere caricate insieme. I simboli denominati si riferiscono ad associazioni già definite. Vedi caricamento avido sotto Associazioni.
  • : select - Per impostazione predefinita, questo è "*" come in "SELECT * FROM", ma può essere modificato se, ad esempio, si desidera eseguire un join ma non includere le colonne unite. Prende una stringa con il frammento SQL SELECT (ad esempio "id, nome").
  • : da - Per impostazione predefinita, questo è il nome tabella della classe, ma può essere modificato in un nome tabella alternativo (o anche il nome di una vista del database).
  • : readonly - Contrassegna i record restituiti in sola lettura in modo che non possano essere salvati o aggiornati.
  • : lock - Un frammento SQL come "FOR UPDATE" o "LOCK IN SHARE MODE". : lock => true fornisce il blocco esclusivo predefinito della connessione, solitamente "FOR UPDATE".

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

+0

Qualcuno modifica questo in modo che i parametri siano più leggibili. –