În acest articol veți învăța elementele de bază ale interogărilor Active Record și veți învăța câteva fundamentale despre SQL pe parcurs. Acesta este destinat începătorilor care doresc să înceapă să învețe mai multe despre interogările bazei de date în Ruby on Rails.
Înregistrarea activă este utilizată pentru interogarea bazei de date. Acesta poate fi folosit cu SQL, PostgresSQL și SQLite. Pentru recuperarea înregistrărilor din baza de date, aveți la dispoziție mai multe metode de căutare. Lucrul cool despre ei este că vă puteți salva probleme de scriere a SQL-ului brut.
Ce face într-adevăr metoda de căutare? Practic trei lucruri: opțiunile furnizate dvs. sunt convertite într-o interogare SQL. Apoi interogarea SQL se execută și prelucrează datele din baza de date. De asemenea, pentru fiecare rând din acea listă de rezultate, obținem obiecte Ruby noi instanțiate ale modelului care corespund interogării.
Dacă nu ați mai jucat cu SQL înainte, voi încerca tot posibilul să vă păstrez lucrurile simple și să vă prezint foarte multe lucruri de bază. Urmați exemplele SQL și încercați să înțelegeți aceste simple întrebări. SQL nu este o știință a rachetelor - sintaxa are nevoie doar de obișnuință. Acest lucru va spera, sperăm, apetitul dvs. pentru a vâna câteva tutoriale utile care să completeze golurile.
Să aruncăm o privire la câteva metode care vă stau la dispoziție:
găsi
primul
ultimul
find_by
toate
find_each
find_in_batches
Unde
Ordin
limită
ofset
grup
având
Toate acestea vor reveni la o instanță ActiveRecord :: Relația
. O ce? Este o clasă care este numită în cadrul module ActiveRecord
, și ne permite să apelăm mai multe metode de interogare și să le lanseze. Acest obiect este inima sintaxei interogării utilizate în Rails. Să verificăm clasa unui astfel de obiect și să ne vedem:
Agent.where (nume: "James Bond") class # => ActiveRecord :: Relație
găsi
Această metodă vă permite să furnizați id-ul primar al unui obiect și să preluați acel obiect unic pentru dvs. Dacă furnizați o serie de ID-uri, puteți prelua și mai multe obiecte.
bond = Agent.find (7)
Selectați "agenți". * FROM "agenți" WHERE "agenți". "Id" =? LIMIT 1 [["id", 7]]
Această linie de SQL specifică faptul că doriți să selectați toate (*
) atribute de la agenţi
tabelați și "filtrați" numai înregistrarea cu id-ul 7. O limită face să se returneze doar o singură înregistrare din baza de date.
primul
, ultimul
În mod surprinzător, acestea vă vor oferi primele și ultimele înregistrări care pot fi identificate de cheia primară. Partea interesantă, totuși, este că puteți furniza un număr opțional care vă întoarce primul sau ultimul număr de înregistrări.
enemy_agents = SpectreAgent.first (10) enemy_agents = SpectreAgent.last (10)
Sub capota, oferiți o nouă limită pentru numărul pe care îl furnizați și ordonați-l să urce sau să coboare.
SELECT "spectreagenti". * FROM "spectreagenti" ORDINEA DE "spectreagenti". "Id" ASC LIMIT 10 SELECT "spectreagenti" * FROM "spectreagenti" ORDER BY "spectreagents"
find_by
Acest căutător returnează primul obiect care corespunde condiției pe care o oferiți.
bond = Agent.find_by (ultim_name: "Bond")
SELECTați "agenți". * FROM "agenți" WHERE "agenți". "Last_name" =? LIMIT 1 [["last_name", "Bond"]]
Evident, trebuie adesea să repetăm o colecție de obiecte cu o anumită ordine de zi. Preluarea unui singur obiect sau a câtorva selectate de mână este plăcută, dar, de cele mai multe ori, ne dorim ca Recordul activ să preia obiecte în loturi.
Arătând utilizatorilor toate tipurile de liste este pâinea și untul pentru cele mai multe aplicații Rails. Ceea ce avem nevoie este un instrument puternic, cu un API convenabil pentru a colecta aceste obiecte pentru noi - sperăm într-o manieră care ne permite să evităm scrierea SQL-ului implicat pe noi înșine de cele mai multe ori.
toate
mi6_agents = Agents.all
Selectați "agenți". * FROM "agenți"
Această metodă este utilă pentru colecții relativ mici de obiecte. Încercați să vă imaginați că faceți acest lucru într-o colecție de utilizatori Twitter. Nu, nu este o idee bună. Ceea ce vrem în schimb este o abordare mai bine reglată pentru mărimi mai mari ale meselor.
Fetching-ul intregii mese nu va fi scalabil! De ce? Deoarece nu am fi cerut doar o grămadă de obiecte, ar trebui să construim un obiect pe rând în acest tabel și să le punem într-o matrice în memorie. Sper că acest lucru nu pare o idee bună! Deci, care este soluția pentru asta? Sarjele! Împărțim aceste colecții în loturi care sunt mai ușor de procesat în memorie. Woo hoo!
Să aruncăm o privire find_each
și find_in_batches
. Ambele sunt similare, dar se comportă diferit în modul în care acestea dau obiecte în blocuri. Ei acceptă o opțiune de a reglementa dimensiunea lotului. Valoarea prestabilită este de 1.000.
find_each
NewRecruit.find_each nu recrutează | recruit.start_hellweek sfârșit
SELECT "newrecruits". * FROM "newrecruits" COMANDA DE "newrecruits". "Id" ASC LIMIT 1000
În acest caz, vom prelua un lot prestabilit de 1.000 de recruți noi, îi vom aloca blocului și îi vom trimite în iad săptămâna unu câte unul. Deoarece loturile împart colecții, le putem spune și de unde să începem prin intermediul colecțiilor start
. Să presupunem că vrem să procesăm 3.000 de recruți posibili dintr-o dată și să începem la 4.000.
NewRecruit.find_each (start: 4000, lot_size: 3000) nu recrutează | recruit.start_hellweek sfârșit
SELECT "newrecruits". * FROM "newrecruits" WHERE ("newrecruits". "Id"> = 4000)
Pentru a reitera, vom recupera mai întâi un lot de 3.000 de obiecte Ruby și apoi le vom trimite în bloc. start
ne permite să specificăm id de înregistrări în care vrem să începem să preluăm acest lot.
find_in_batches
Acest lucru dă lotul său ca o matrice a blocului - îl trece pe un alt obiect care preferă să se ocupe de colecții. SQL-ul este același aici.
NewRecruit.find_in_batches (start: 2700, lot_size: 1350) nu | recrutează | field_kitchen.prepare_food (recruți) sfârșit
Unde
Trebuie să mergem Unde
înainte de a continua mai departe. Aceasta ne permite să specificăm condițiile care limitează numărul de înregistrări returnate de interogările noastre - un filtru pentru "unde" pentru a prelua înregistrări din baza de date. Dacă ați jucat cu SQL UNDE
clauze, atunci s-ar putea să vă simțiți doar acasă - același lucru cu acest ambalaj Ruby.
În SQL, acest lucru ne permite să specificăm ce rând de tabele dorim să afectează, în esență unde îndeplinește anumite criterii. Aceasta este o clauză opțională, apropo. În SQL-ul brut de mai jos, selectăm numai recruți care sunt orfani prin intermediul UNDE
.
Selectați un rând specific dintr-o tabelă.
SELECT * FROM Recruți WHERE FamilyStatus = 'Orphan';
Prin intermediul Unde
, puteți specifica condițiile cu șiruri de caractere, hash-uri sau matrice. Punând toate acestea împreună, Active Record vă permite să filtrați pentru astfel de condiții:
promising_candidates = Recruit.where ("family_status = 'orphan'")
SELECTați "recruți". * FROM "recrutează" WHERE (family_status = 'orphan')
Destul de curat, nu? Vreau să menționez că aceasta este încă o operațiune de găsire - specificăm cum dorim să filtram imediat această listă. Din lista tuturor recruților, aceasta va întoarce o listă filtrată de candidați orfani. Acest exemplu este o condiție string. Stați departe de condițiile string string, deoarece acestea nu sunt considerate sigure datorită vulnerabilității lor la injecții SQL.
În exemplul de mai sus, am pus orfan
variabilă în șir cu condițiile. Aceasta este considerată o practică proastă deoarece nu este sigură. Trebuie să scăpăm de această variabilă pentru a evita această vulnerabilitate de securitate. Ar trebui să citiți despre injecția SQL dacă aceasta este știrea totală pentru dvs. - baza dvs. de date ar putea să depindă de aceasta.
promising_candidates = Recruit.where ("family_status =?", "orphan" ")
?
va fi înlocuit ca valoare condiție de către următoarea valoare din lista de argumente. Deci, semnul întrebării este un loc de substituție. De asemenea, puteți specifica mai multe condiții cu multiple ?
și le lansează împreună. Într-un scenariu real, am folosi un paragraf de tip hash ca acesta:
promising_candidates = Recruit.where ("family_status =?", params [: recruits])
Dacă aveți un număr mare de condiții variabile, ar trebui să utilizați condiții de substituție cheie / valoare.
promising_candidates = Recruit.where ("family_status =: preferat_status ȘI iq> =: required_iq AND charming =: lady_killer", preferat_status: 'orphan', required_iq: 140, lady_killer: true)
SELECTați "recruți". * FROM "recrutează" WHERE (family_status = 'orphan' ȘI iq> = 140 AND lady_killer = true)
Exemplul de mai sus este, desigur, prostie, dar arată în mod clar beneficiile notației de substituție. Notatia hash, în general, este cu siguranta cea mai citibila.
promising_candidates = Recruit.where (familie_status: "orfan") promising_candidates = Recruit.where ("fermecător": adevărat)
După cum puteți vedea, puteți merge cu simboluri sau șiruri de caractere. Să închidem această secțiune cu intervale și condiții negative prin NOT.
promising_candidates = Recruit.where (data nașterii: ('1994-01-01' ... '2000-01-01'))
Două puncte și puteți stabili orice interval de care aveți nevoie.
promising_candidates = Recruit.where.not (personaj: "laș")
Poți să-ți dai drumul nu
pe Unde
pentru a filtra toți lașii și pentru a obține numai rezultate care nu au acel atribut specific, nedorit. Sub capotă, a !=
respinge WHERE "filtrul".
SELECTați "recruți". * FROM "recrutează" WHERE ("recrutează". "Caracter"! =?) [["Caracter", "
Ordin
Ca să nu te plictisești cu asta, să facem asta unul rapid.
candidați = Recruit.order (: date_of_birth)
candidați = Recruit.order (: date_of_birth,: desc)
aplica : asc
sau : desc
pentru ao sorta în consecință. În principiu, așa că hai să mergem mai departe!
limită
Puteți reduce numărul de înregistrări returnate la un anumit număr. Așa cum am menționat mai devreme, de cele mai multe ori nu veți avea nevoie de toate înregistrările returnate. Exemplul de mai jos vă va oferi primele cinci recruți în baza de date - primele cinci ID-uri.
five_candidates = Recruit.limit (5)
SELECTați "recruți". * FROM "recrutează" LIMIT 5
ofset
Dacă v-ați întrebat vreodată cum funcționează paginarea sub capotă, limită
și ofset
-în conjuncție - faceți munca grea. limită
poate sta singur, dar ofset
depinde de primul.
Setarea unui offset este utilă în cea mai mare parte pentru paginare și vă permite să săriți numărul dorit de rânduri din baza de date. Pagina a doua a unei liste de candidați ar putea fi privită în felul următor:
Recruit.limit (20) .offset (20)
SQL ar arăta astfel:
SELECTați "recruți". * FROM "recrutează" LIMIT 20 OFFSET 20
Din nou, selectăm toate coloanele din Recruta
baza de date, limitând înregistrările returnate la 20 de obiecte Ruby din Class Recruit și sărind peste primele 20.
Să presupunem că vrem o listă de recruți care sunt grupați după IQ-urile lor. În SQL, acest lucru ar putea arăta așa.
SELECTAȚI "recruți" * FROM "recrutează" GROUP BY "recruți" "iq"
Acest lucru vă va oferi o listă în care veți vedea care sunt posibilii recruți care au un IQ de 120, apoi un alt grup de 140, și așa mai departe - indiferent de IQ-urile lor și câți ar putea fi sub un anumit număr. Deci, atunci când doi recruți au același IQ de 130, ei vor fi grupați împreună.
O altă listă ar putea fi grupată de posibili candidați care suferă de claustrofobie, frică de înălțimi sau care nu sunt sănătoși din punct de vedere medical pentru scufundări. Interogarea Active Record ar arăta pur și simplu astfel:
grup
Candidate.group (: iq)
Atunci când numărăm numărul de candidați, revenim la un hash foarte util.
Candidate.group (: iq) .count # => 130 => 7, 134 => 4, 135 => 3, 138 => 2, 140 => 1, 141 => 1
Acolo mergem - avem șapte recruți posibili cu un IQ de 130 și doar unul cu 141. SQL-ul rezultat ar arăta astfel:
SELECT COUNT (*) AS count_all, iq AS iq DIN "candidați" GRUP DE "candidați" "iq"
Piesa importantă este A SE GRUPA CU
parte. După cum puteți vedea, folosim tabelul pentru candidați pentru a obține identitatea lor. Ceea ce puteți observa din acest exemplu simplu este cât de mult mai convenabil versiunile Active Record sunt citite și scrise. Imaginați-vă că faceți acest lucru manual cu exemple mai extravagante. Sigur, uneori trebuie să faceți asta, dar tot timpul este clar o durere pe care o putem evita cu plăcere.
având
Putem specifica acest grup folosind mai mult AVÂND
-un fel de filtru pentru grup
. In acest sens, având
este un fel de UNDE
clauza pentru GRUP
. Cu alte cuvinte, având
este dependentă de utilizare grup
.
Recruit.having ('iq>?', 134) .group (: iq)
SELECTAȚI "recruți" * FROM "recrutează" GROUP BY "recruți" "iq" HAVING iq> '134'
Acum am grupat candidații noștri în liste de persoane care au un IQ minim de 135. Să le numărăm pentru a obține câteva statistici:
Recrutarea are (iq>? ', 134) .group (: iq) .count # => 135 => 3, 138 => 2, 140 => 1, 141 =
SELECT COUNT (*) AS count_all, iq AS iq FROM "recrutează" GROUP BY "recruți" "iq" HAVING iq> '134'
Am putea, de asemenea, să le amestecăm și să le potrivim și să vedem, de exemplu, care dintre candidații care au IQ-uri mai mari de 140 sunt legați în relații sau nu.
Recruit.having ('iq>?', 140) .group (: familie_status)
SELECT "recruți" * FROM "recrutează" GROUP BY "recruți". "Family_status" HAVING iq> '140'
Numărarea acestor grupuri este acum prea ușoară:
Recruit.having ('iq>?', 140) .group (: family_status) .count # => "căsătorit" => 2, "single" => 1
SELECT COUNT (*) AS count_all, family_status AS family_status FROM "recrutează" GROUP BY "recruți". "Family_status" HAVING iq> '140'
Sper că a fost o primă analiză utilă a ceea ce Active Record are de oferit pentru a vă face eforturile de interogare cât mai ușor de citit și mai convenabil. În ansamblu, aș spune că este un pachet excelent care vă împiedică să scrieți manual SQL de cele mai multe ori.
În următorul articol, vom examina câțiva descendenți implicați și vom extinde ceea ce am învățat până acum.