Query in Rails, parte 1

In questo articolo imparerai le basi delle query di Active Record e imparerai alcune nozioni di base su SQL lungo il percorso. È rivolto ai principianti che vogliono iniziare a imparare di più sulle query di database in Ruby on Rails.

Temi

  • Singoli oggetti
  • Oggetti multipli
  • condizioni
  • ordinazione
  • limiti
  • Gruppo e Avere

Active Record è usato per interrogare il database. Può essere utilizzato con SQL, PostgresSQL e SQLite. Per recuperare i record dal tuo database, hai a disposizione diversi metodi di ricerca. La cosa bella di loro è che puoi risparmiarti la fatica di scrivere SQL raw. 

Che cosa fa veramente un metodo finder? Fondamentalmente tre cose: le opzioni fornite vengono convertite in una query SQL. Quindi la query SQL viene eseguita e recupera i dati dal database. Inoltre, per ogni riga in quell'elenco dei risultati, otteniamo oggetti Ruby di nuova istanza del modello che corrisponde alla query. 

Se non hai mai giocato con SQL, farò del mio meglio per semplificare le cose e farti conoscere le basi. Segui gli esempi SQL e cerca di dare un senso a queste semplici domande. SQL non è davvero una scienza missilistica, la sintassi richiede un po 'di tempo per abituarsi. Speriamo che questo stimoli l'appetito a dare la caccia ad alcuni tutorial utili che riempiono gli spazi vuoti. 

Diamo un'occhiata ad alcuni metodi che sono a vostra disposizione:

  • trova
  • primo
  • scorso
  • find_by
  • tutti
  • find_each
  • find_in_batches
  • dove
  • ordine
  • limite
  • compensare
  • gruppo
  • avendo

Tutti questi restituiranno un'istanza di ActiveRecord :: Relation. A cosa? È una classe che è assegnata ai nomi all'interno del modulo ActiveRecord, e ci consente di chiamare più metodi di query e concatenarli. Questo oggetto è il cuore della sintassi della query utilizzata in Rails. Controlliamo la classe di un tale oggetto e vediamo da soli:

Rails

Agent.where (nome: 'James Bond'). Class # => ActiveRecord :: Relazione

Singoli oggetti

  • trova

Questo metodo ti consente di fornire l'id principale di un oggetto e recupera quel singolo oggetto per te. Se fornisci una serie di ID, puoi anche recuperare più oggetti.

Rails

bond = Agent.find (7)

SQL

SELEZIONA "agenti". * DA "agenti" DOVE "agenti". "Id" =? LIMIT 1 [["id", 7]]

Questa riga di stati SQL che si desidera selezionare tutto (*) attributi dal agenti tabella e "filtrare" solo il record con l'ID 7. Un limite consente di restituire solo un singolo record dal database.

  • primo, scorso

Non sorprendentemente, questi ti daranno il primo e l'ultimo record che possono essere identificati dalla loro chiave primaria. La parte interessante, tuttavia, è che puoi fornire un numero opzionale che ti restituisce il primo o l'ultimo di quel numero di record. 

Rails

enemy_agents = SpectreAgent.first (10) enemy_agents = SpectreAgent.last (10)

Sotto il cofano, stai fornendo un nuovo limite per il numero che hai fornito e lo ordini in ordine crescente o decrescente.

SQL

SELECT "spettreagenti". * DA "spettreagenti" ORDER BY "spettreagenti". "Id" ASC LIMIT 10 SELECT "spettreagenti". * DA "spettreagenti" ORDER BY "spettreagenti". "Id" LIMITI DEL DESCRIZIONE 10
  • find_by

Questo finder restituisce il primo oggetto che corrisponde alla condizione fornita.

Rails

bond = Agent.find_by (last_name: 'Bond')

SQL

SELECT "agenti". * DA "agenti" DOVE "agenti". "Last_name" =? LIMIT 1 [["last_name", "Bond"]]

Oggetti multipli

Ovviamente, abbiamo spesso bisogno di scorrere su una collezione di oggetti con qualche ordine del giorno. Recuperare manualmente un singolo oggetto o pochi selezionati è bello, ma il più delle volte, vogliamo che Active Record recuperi gli oggetti in lotti. 

Mostrare agli utenti tutti i tipi di elenchi è il pane e il burro per la maggior parte delle app Rails. Ciò di cui abbiamo bisogno è uno strumento potente con un'API conveniente per raccogliere questi oggetti per noi, si spera in un modo che ci consenta di evitare di scrivere noi stessi l'SQL coinvolto per la maggior parte del tempo.

  • tutti

Rails

mi6_agents = Agents.all

SQL

SELEZIONA "agenti". * DA "agenti"

Questo metodo è utile per raccolte di oggetti relativamente piccole. Prova a immaginare di farlo su una raccolta di tutti gli utenti di Twitter. No, non è una buona idea. Quello che vogliamo invece è un approccio più sintonizzato per tavoli di dimensioni maggiori. 

Recuperare l'intero tavolo non sta andando in scala! Perché? Poiché non richiederemo solo un mucchio di oggetti, avremmo anche bisogno di creare un oggetto per riga in questa tabella e metterli in una matrice in memoria. Spero che non sembri una buona idea! Quindi qual è la soluzione per questo? Lotti! Stiamo dividendo queste raccolte in lotti che sono più facili da elaborare per la memoria. Woohoo!

Diamo un'occhiata a find_each e find_in_batches. Entrambi sono simili ma si comportano diversamente nel modo in cui producono oggetti in blocchi. Accettano un'opzione per regolare la dimensione del batch. Il valore predefinito è 1.000.

  • find_each

Rails

NewRecruit.find_each do | recluta | recruit.start_hellweek end

SQL

SELEZIONA "newrecruits". * FROM "newrecruits" ORDER BY "newrecruits". "Id" ASC LIMIT 1000

In questo caso, recuperiamo un batch predefinito di 1.000 nuovi reclutati, li consegniamo al blocco e li inviamo alla settimana dell'inferno, uno per uno. Poiché i batch stanno affettando le raccolte, possiamo anche dire loro da dove iniziare inizio. Diciamo che vogliamo processare 3.000 possibili reclute in una volta sola e vogliamo iniziare da 4.000.

Rails

NewRecruit.find_each (start: 4000, batch_size: 3000) do | recruit | recruit.start_hellweek end

SQL

SELEZIONA "newrecruits". * FROM "newrecruits" WHERE ("newrecruits". "Id"> = 4000) ORDINA DA "newrecruits". "Id" ASC LIMIT 3000

Per reiterare, prima recuperiamo un lotto di 3.000 oggetti Ruby e poi li inviamo nel blocco. inizio ci permette di specificare l'id dei record in cui vogliamo iniziare a recuperare questo batch.

  • find_in_batches

Questo produce il suo batch come una matrice per il blocco: lo passa su un altro oggetto che preferisce occuparsi delle collezioni. L'SQL è lo stesso qui.

Rails

NewRecruit.find_in_batches (inizio: 2700, batch_size: 1350) do | reclute | field_kitchen.prepare_food (reclute) fine

condizioni

  • dove

Dobbiamo andare oltre dove prima di continuare ulteriormente. Questo ci consente di specificare le condizioni che limitano il numero di record restituiti dalle nostre query, un filtro per "dove" per recuperare i record dal database. Se hai giocato con SQL DOVE clausole allora potresti sentirti a casa, stessa cosa con questo involucro di Ruby. 

In SQL, questo ci permette di specificare quale riga della tabella vogliamo influenzare, fondamentalmente dove incontra una sorta di criteri. Questa è una clausola facoltativa, tra l'altro. Nell'SQL grezzo sottostante, selezioniamo solo le reclute che sono orfane tramite DOVE

Seleziona una riga specifica da una tabella.

SQL

SELECT * FROM Recruits WHERE FamilyStatus = 'Orfano';

attraverso dove, puoi specificare le condizioni con stringhe, hash o array. Mettendo insieme tutto questo, Active Record ti consente di filtrare per condizioni come questa:

Rails

promising_candidates = Recruit.where ("family_status = 'orphan'")

SQL

SELEZIONA "reclute". * FROM "reclute" DOVE (family_status = 'orfano')

Abbastanza pulito, giusto? Voglio menzionare che si tratta ancora di un'operazione di individuazione. Specifichiamo solo il modo in cui vogliamo filtrare questa lista immediatamente. Dall'elenco di tutte le reclute, questo restituirà un elenco filtrato di candidati orfani. Questo esempio è una condizione di stringa. Stare lontano da condizioni di stringa pura in quanto non sono considerati sicuri a causa della loro vulnerabilità alle iniezioni SQL.

Argument Safety

Nell'esempio sopra, inseriamo il orfano variabile nella stringa con le condizioni. Questa è considerata una cattiva pratica perché non è sicura. Dobbiamo evitare la variabile per evitare questa vulnerabilità della sicurezza. Dovresti leggere informazioni sull'iniezione SQL se questa è una notizia totale - il tuo database potrebbe dipendere da esso.

Rails

promising_candidates = Recruit.where ("family_status =?", "orphan" ")

Il ? verrà sostituito come valore della condizione dal valore successivo nell'elenco degli argomenti. Quindi il punto interrogativo è fondamentalmente un segnaposto. Puoi anche specificare più condizioni con più elementi ? e incatenarli insieme. In uno scenario reale, useremmo un hash params come questo:

promising_candidates = Recruit.where ("family_status =?", params [: reclute])

Se si dispone di un numero elevato di condizioni variabili, è necessario utilizzare condizioni segnaposto chiave / valore.

Rails

promising_candidates = Recruit.where ("family_status =: preferred_status AND iq> =: required_iq AND charming =: lady_killer", preferred_status: "orphan", required_iq: 140, lady_killer: true)

SQL

SELEZIONA "reclute". * FROM "reclute" WHERE (family_status = 'orfano' AND iq> = 140 AND lady_killer = true)

L'esempio sopra è sciocco, ovviamente, ma mostra chiaramente i benefici della notazione del segnaposto. La notazione hash, in generale, è sicuramente la più leggibile.

Rails

promising_candidates = Recruit.where (family_status: 'orphan') promising_candidates = Recruit.where ('charming': true)

Come puoi vedere, puoi andare con simboli o stringhe. Chiudiamo questa sezione con intervalli e condizioni negative tramite NOT.

Rails

promising_candidates = Recruit.where (compleanno: ('1994-01-01' ... '2000-01-01'))

Due punti e puoi stabilire qualsiasi intervallo di cui hai bisogno.

promising_candidates = Recruit.where.not (personaggio: 'codardo')

Puoi rimboccare il non sul dove per filtrare tutti i codardi e ottenere solo risultati che non hanno quell'attributo specifico e indesiderato. Sotto il cofano, a != nega il "filtro" DOVE.

SQL

SELEZIONA "reclute". * FROM "reclute" WHERE ("reclute". "Carattere"! =?) [["Carattere", "codardo"]]

ordinazione

  • ordine

Per non annoiarvi a morte con esso, rendiamolo veloce.

candidati = Recluta.ordine (: data_di_birth)
candidati = Recluta.ordine (: date_of_birth,: desc)

Applicare : asc o : desc per ordinarlo di conseguenza. Questo è fondamentalmente, quindi andiamo avanti!

limiti

  • limite

È possibile ridurre il numero di record restituiti a un numero specifico. Come accennato in precedenza, la maggior parte delle volte non è necessario restituire tutti i record. L'esempio qui sotto ti darà le prime cinque reclute nel database: i primi cinque id.

Rails

five_candidates = Recruit.limit (5) 

SQL

SELEZIONA "reclute". * FROM "reclute" LIMIT 5
  • compensare

Se ti sei mai chiesto come funziona la paginazione sotto il cofano, limite e compensare-in congiunzione: fai il duro lavoro. limite può stare da solo, ma compensare dipende dal primo.

L'impostazione di un offset è utile soprattutto per la paginazione e consente di saltare il numero desiderato di righe nel database. La seconda pagina di un elenco di candidati potrebbe essere consultata in questo modo:

Rails

Recruit.limit (20) .Offset (20)

L'SQL sarebbe simile a questo:

SELEZIONA "reclute". * FROM "reclute" LIMIT 20 OFFSET 20

Ancora una volta, stiamo selezionando tutte le colonne dal Recluta modello di database, limitando i record restituiti a 20 oggetti Ruby di Class Recruit e saltando oltre i primi 20.

Gruppo e Avere

Diciamo che vogliamo un elenco di reclute raggruppate in base al loro QI. In SQL, questo potrebbe sembrare qualcosa del genere.

SELEZIONA "reclute". * FROM "reclute" GROUP BY "reclute". "Iq"

Questo ti farebbe avere una lista dove vedrai quali reclute possibili hanno un QI di diciamo 120, e poi un altro gruppo di dire 140, e così via - qualunque sia il loro QI e quanti potrebbero cadere in un numero specifico. Quindi, quando due reclute hanno lo stesso QI di 130, sarebbero raggruppate insieme. 

Un altro elenco potrebbe essere raggruppato da possibili candidati che soffrono di claustrofobia, paura dell'altezza o che non sono idonei dal punto di vista medico per le immersioni. La query Active Record sarebbe semplicemente simile a questa:

  • gruppo

Rails

Candidate.group (: iq)

Quando contiamo il numero di candidati, otteniamo un hash molto utile.

Rails

Candidate.group (: iq) .count # => 130 => 7, 134 => 4, 135 => 3, 138 => 2, 140 => 1, 141 => 1

Eccoci, abbiamo sette reclute possibili con un QI di 130 e solo uno con 141. L'SQL risultante sarebbe simile a questo:

SQL

SELECT COUNT (*) AS count_all, iq AS iq DA "candidati" GROUP BY "candidati". "Iq"

Il pezzo importante è il RAGGRUPPA PER parte. Come puoi vedere, usiamo la tabella dei candidati per ottenere i loro id. Quello che si può anche osservare da questo semplice esempio è quanto molto più conveniente leggere e scrivere le versioni di Active Record. Immagina di farlo a mano su esempi più stravaganti. Certo, a volte devi, ma tutto il tempo è chiaramente un dolore che possiamo evitare volentieri.

  • avendo

Possiamo specificare questo gruppo ancora di più usando VISTA-una sorta di filtro per il gruppo. In tal senso, avendo è una specie di DOVE clausola per GRUPPO. In altre parole, avendo dipende dall'uso gruppo.

Rails

Recruit.having ('iq>?', 134) .group (: iq)

SQL

SELEZIONA "reclute". * FROM "reclute" GROUP BY "reclute". "Iq" HAVING iq> "134"

Ora abbiamo raggruppato i nostri candidati in elenchi di persone con un QI minimo di 135. Contiamo per ottenere alcune statistiche:

Rails

Recruit.having ('iq>?', 134) .group (: iq) .count # => 135 => 3, 138 => 2, 140 => 1, 141 => 1

SQL

SELECT COUNT (*) AS count_all, iq AS iq FROM "recruits" GROUP BY "reclute". "Iq" HAVING iq> '134'

Potremmo anche mischiarli e vedere, per esempio, quali candidati con QI superiore a 140 sono legati in relazioni o no. 

Rails

Recruit.having ('iq>?', 140) .group (: family_status)

SQL

SELEZIONA "reclute". * FROM "reclute" GROUP BY "reclute". "Family_status" HAVING iq> "140"

Il conteggio di questi gruppi è ora fin troppo facile:

Rails

Recruit.having ('iq>?', 140) .group (: family_status) .count # => "married" => 2, "single" => 1

SQL

SELECT COUNT (*) AS count_all, family_status AS family_status FROM "recruits" GROUP BY "recruits". "Family_status" HAVING iq> "140"

Pensieri finali

Spero che questo sia stato un primo sguardo utile a ciò che Active Record ha da offrire per rendere i tuoi sforzi di ricerca il più leggibili e convenienti possibile. Nel complesso, direi che è un eccellente wrapper che ti impedisce di scrivere SQL a mano la maggior parte del tempo. 

Nel prossimo articolo, esamineremo un paio di ricercatori più interessati e approfondiremo ciò che abbiamo imparato finora.