I denne artikkelen vil du lære grunnleggende om Active Record-spørringer og lære noen grunnleggende om SQL underveis. Det er rettet mot nybegynnere som ønsker å begynne å lære mer om databasespørsmål i Ruby on Rails.
Active Record brukes til å spørre databasen. Den kan brukes med SQL, PostgresSQL og SQLite. For å hente poster fra databasen har du flere søkemetoder til din disposisjon. Den kule tingen om dem er at du kan spare deg selv for å skrive rå SQL.
Hva gjør en finder metode virkelig? I utgangspunktet tre ting: dine angitte alternativer blir konvertert til en SQL-spørring. Da blir SQL-spørringen utført og henter data fra databasen. Også, for hver rad i resultatlisten, får vi nylig instantierte Ruby-objekter av modellen som tilsvarer spørringen.
Hvis du ikke har spilt med SQL før, vil jeg prøve mitt beste for å holde ting enkelt og introdusere deg til det aller grunnleggende. Følg SQL-eksemplene, og prøv å gjøre mening for disse enkle spørringene. SQL er ingen rakettvitenskap virkelig - syntaksen tar bare litt å bli vant til. Dette vil forhåpentligvis gjøre din appetitt til å jakte på noen nyttige opplæringsprogrammer som fyller i blankene.
La oss ta en titt på noen få metoder som står til din disposisjon:
finne
først
siste
find_by
alle
find_each
find_in_batches
hvor
rekkefølge
grense
offset
gruppe
ha
Alle disse vil returnere en forekomst av Active :: Relation
. En hva? Det er en klasse som er namespaced innenfor modul ActiveRecord
, og det tillater oss å ringe flere spørringsmetoder og kjede dem. Dette objektet er hjertet av spørringssyntaxen som brukes i Rails. La oss sjekke klassen av et slikt objekt og se for oss selv:
Agent.where (navn: 'James Bond'). Klasse # => ActiveRecord :: Relasjon
finne
Denne metoden lar deg levere det primære ID til et objekt og henter det enkelte objektet for deg. Hvis du gir en rekke ids, kan du også hente flere objekter.
bond = Agent.find (7)
VELG "agenter". * FRA "agenter" hvor "agenter". "Id" =? LIMIT 1 [["id", 7]]
Denne linjen av SQL angir at du vil velge alle (*
) attributter fra agenter
bord og "filter" ut bare posten med id 7. En grense gjør at den bare returnerer en enkelt post fra databasen.
først
, siste
Overraskende vil disse gi deg de første og siste postene som kan identifiseres av deres primære nøkkel. Den interessante delen er imidlertid at du kan gi et valgfritt nummer som gir deg det første eller siste av det antall poster.
enemy_agents = SpectreAgent.first (10) enemy_agents = SpectreAgent.last (10)
Under hetten gir du en ny grense for nummeret du oppgir og bestiller det stigende eller synkende.
SELECT "spectreagents". * FRA "spectreagents" ORDER BY "spectreagents". "Id" ASC LIMIT 10 SELECT "spectreagents". * FRA "spectreagents" ORDER BY "spectreagents". "Id" DESC LIMIT 10
find_by
Denne søkeren returnerer det første objektet som samsvarer med tilstanden du oppgir.
obligasjon = Agent.find_by (sistenavn: 'Bond')
VELG "agenter". * FRA "agenter" hvor "agenter". "Last_name" =? LIMIT 1 [["siste navn", "Bond"]]
Tydeligvis må vi ofte deterere over en samling objekter med noen agendaer. Å hente en enkelt gjenstand eller et utvalgte få for hånd er fint, men oftere enn ikke, vi ønsker Active Record å hente objekter i grupper.
Viser brukere alle slags lister er brød og smør for de fleste Rails-apper. Det vi trenger er et kraftig verktøy med en praktisk API for å samle disse objektene for oss - forhåpentligvis på en måte som lar oss unngå å skrive den involverte SQL oss mesteparten av tiden.
alle
mi6_agents = Agents.all
VELG "agenter". * FRA "agenter"
Denne metoden er nyttig for relativt små samlinger av objekter. Prøv å forestille deg å gjøre dette på en samling av alle Twitter-brukere. Nei, ikke en god ide. Det vi ønsker i stedet er en mer finjustert tilnærming til større bordstørrelser.
Å hente hele bordet kommer ikke til å skalere! Hvorfor? Fordi vi ikke bare vil be om en masse objekter, må vi også bygge ett objekt per rad i denne tabellen og sette dem inn i en matrise i minnet. Jeg håper dette ikke høres ut som en god ide! Så hva er løsningen for dette? Batcher! Vi deler disse samlingene i batcher som er enklere på minne for behandling. woohoo!
La oss ta en titt på find_each
og find_in_batches
. Begge er liknende, men oppfører seg annerledes i hvordan de gir objekter i blokker. De aksepterer et alternativ for å regulere batchstørrelsen. Standard er 1000.
find_each
NewRecruit.find_each do | rekruttere | recruit.start_hellweek end
VELG "newrecruits". * FRA "newrecruits" BESTILL BY "newrecruits". "Id" ASC LIMIT 1000
I dette tilfellet henter vi en standard sats på 1000 nye rekrutter, gir dem til blokken, og sender dem til helvete-en etter en. Fordi satser slicerer opp samlinger, kan vi også fortelle dem hvor de skal starte via start
. La oss si at vi ønsker å behandle 3.000 mulige rekrutter på en gang og ønsker å starte på 4.000.
NewRecruit.find_each (start: 4000, batch_size: 3000) do | recruit | recruit.start_hellweek end
VELG "newrecruits". * FRA "newrecruits" WHERE ("newrecruits". "Id"> = 4000) BESTILL AV "newrecruits". "ID" ASC LIMIT 3000
For å gjenta, henter vi først en serie 3 000 rubyobjekter og sender dem deretter inn i blokken. start
Lar oss spesifisere ID-nummeret der vi ønsker å begynne å hente denne batchen.
find_in_batches
Denne gir sin batch som en matrise til blokken - den overfører den til et annet objekt som foretrekker å håndtere samlinger. SQL er den samme her.
NewRecruit.find_in_batches (start: 2700, batch_size: 1350) rekruttere | field_kitchen.prepare_food (rekrutter) slutten
hvor
Vi må gå over hvor
før vi fortsetter videre. Dette lar oss spesifisere forhold som begrenser antall poster som returneres av våre spørsmål - et filter for "hvor" for å hente poster fra databasen. Hvis du har spilt med SQL HVOR
klausuler så kan du bare føle deg som hjemme - det samme med denne Ruby-wrappen.
I SQL gir dette oss mulighet til å spesifisere hvilken tabellrad vi vil påvirke, i utgangspunktet hvor den oppfyller noen slags kriterier. Dette er en valgfri klausul, forresten. I den røde SQL nedenfor velger vi bare rekrutter som er foreldreløse via HVOR
.
Velg en bestemt rad fra et bord.
VELGE * FRA Rekrutter WHERE FamilyStatus = 'Orphan';
via hvor
, Du kan spesifisere forhold med strenger, hashes eller arrayer. Ved å legge alt dette sammen, lar Active Record deg filtrere for slike forhold som dette:
promising_candidates = Recruit.where ("family_status = 'foreldreløs'")
VELG "rekrutter". * FRA "rekrutter" hvor (family_status = 'foreldreløse')
Ganske pent, ikke sant? Jeg vil nevne at dette fortsatt er en funnoperasjon - vi angir bare hvordan vi vil filtrere denne listen med en gang. Fra listen over alle rekrutter, vil dette returnere en filtrert liste over foreldreløse kandidater. Dette eksemplet er en strengtilstand. Hold deg unna rene strengforhold, siden de ikke regnes som sikre på grunn av deres sårbarhet mot SQL-injeksjoner.
I eksemplet ovenfor legger vi foreldreløs
variabel inn i strengen med betingelsene. Dette regnes som en dårlig praksis fordi den er usikker. Vi må unnslippe variabelen for å unngå dette sikkerhetsproblemet. Du bør lese om SQL-injeksjon hvis dette er totalt nytt for deg - databasen din kan være avhengig av den.
promising_candidates = Recruit.where ("family_status =?", "foreldreløs")
De ?
vil bli erstattet som betingelsesverdien med neste verdi i argumentlisten. Så spørsmålet er en plassholder i utgangspunktet. Du kan også angi flere forhold med flere ?
og kjede dem sammen. I et virkelighetsscenario vil vi bruke en params hash slik:
promising_candidates = Recruit.where ("family_status =?", params [: rekrutter])
Hvis du har et stort antall variable forhold, bør du bruke nøkkel / verdi plassholderforhold.
promising_candidates = Recruit.where ("family_status =: preferred_status OG iq> =: required_iq OG sjarmerende =: lady_killer", foretrukket_status: "foreldreløs", required_iq: 140, lady_killer: true)
VELG "rekrutter". * FRA "rekrutter" hvor (family_status = 'foreldreløse' og iq> = 140 og lady_killer = true)
Eksemplet ovenfor er selvfølgelig dumt, men det viser tydelig fordelene med plassholdernotasjonen. The hash notasjon, generelt, er definitivt den mer lesbare.
promising_candidates = Recruit.where (family_status: 'foreldreløs') promising_candidates = Recruit.where ('charming': true)
Som du kan se, kan du gå med symboler eller strenger opp til deg. La oss lukke denne delen med intervaller og negative forhold via IKKE.
promising_candidates = Recruit.where (fødselsdag: ('1994-01-01' ... '2000-01-01'))
To prikker, og du kan etablere hvilket som helst område du trenger.
promising_candidates = Recruit.where.not (tegn: 'coward')
Du kan tømme ikke
på hvor
å filtrere ut alle fiender og få bare resultater som ikke har det spesifikke, uønskede attributtet. Under hetten, a !=
negerer WHERE "filter".
SELECT "rekrutter". * FRA "rekrutter" hvor ("rekrutterer". "Karakter"! =?) [["Karakter", "fei"]]
rekkefølge
For ikke å bore deg til døden med det, la oss gjøre dette til en rask en.
kandidater = rekruttere.order (: dato_av_birth)
kandidater = rekruttere.order (: dato_av_birth,: desc)
Søke om : ASC
eller : desc
å sortere det tilsvarende. Det er i utgangspunktet det, så la oss gå videre!
grense
Du kan redusere antall returnerte poster til et bestemt nummer. Som nevnt tidligere, vil du mesteparten av tiden ikke trenger alle poster returnert. Eksempelet nedenfor gir deg de fem første rekrutter i databasen - de første fem ids.
five_candidates = Recruit.limit (5)
VELG "rekrutter". * FRA "rekrutter" LIMIT 5
offset
Hvis du noen gang har lurt på hvordan paginering virker under hetten, grense
og offset
-i sammenheng - gjør det harde arbeidet. grense
kan stå alene, men offset
Avhenger av den tidligere.
Angi en forskyvning er mest nyttig for paginering, og lar deg hoppe over ønsket antall rader i databasen. Side to av en liste over kandidater kan bli sett opp slik:
Recruit.limit (20) .offset (20)
SQL ser slik ut:
VELG "rekrutter". * FRA "rekrutter" LIMIT 20 OFFSET 20
Igjen velger vi alle kolonnene fra Rekruttere
databasemodell, og begrenser registreringene tilbake til 20 Ruby-objekter av Class Recruit og hopper over de første 20 årene.
La oss si at vi vil ha en liste over rekrutter som er gruppert av deres IQ. I SQL kan dette se ut som dette.
VELG "rekrutter". * FRA "rekrutter" GRUPPE AV "rekrutter". "IQ"
Dette vil gi deg en liste hvor du ser hvilke mulige rekrutterer som har en IQ av la oss si 120, og så en annen gruppe med 140, og så videre - uansett hva deres IQ er, og hvor mange vil falle under et bestemt nummer. Så når to rekrutter har samme IQ på 130, vil de bli gruppert sammen.
En annen liste kan grupperes av mulige kandidater som lider av klaustrofobi, høyderyke, eller som er medisinsk ikke egnet til dykking. Aktivt rekordspørsmålet ville ganske enkelt se slik ut:
gruppe
Candidate.group (: iq)
Når vi teller antall kandidater, får vi tilbake en veldig nyttig hash.
Candidate.group (: iq) .count # => 130 => 7, 134 => 4, 135 => 3, 138 => 2, 140 => 1, 141 => 1
Der vi går - vi fikk syv mulige rekrutter med en IQ på 130 og bare en med 141. Den resulterende SQL ville se slik ut:
SELECT COUNT (*) AS count_all, iq AS iq FRA "kandidater" GRUPP AV "kandidater". "Iq"
Det viktige stykket er GRUPPE AV
del. Som du kan se bruker vi kandidatbordet for å få sine ids. Det du også kan observere fra dette enkle eksempelet, er hvor mye lettere Active Record-versjonene leser og skriver. Tenk deg å gjøre dette for hånd på flere ekstravagante eksempler. Sikker, noen ganger må du, men hele tiden er det tydelig en smerte vi kan gjerne unngå.
ha
Vi kan spesifisere denne gruppen enda mer ved å bruke HAR
-slags filter for gruppe
. I den forstand, ha
er en slags HVOR
klausul for GRUPPE
. Med andre ord, ha
er avhengig av bruk gruppe
.
Rekruttere.having ('iq>?', 134) .gruppe (: iq)
VELG "rekrutter". * FRA "rekrutter" GRUPP AV "rekrutter". "IQ" HAR IQ> '134'
Vi har nå gruppert våre kandidater i lister over personer som har en minimum IQ på 135. La oss telle dem for å få litt statistikk:
Recruit.having ('iq>?', 134) .group (: iq) .count # => 135 => 3, 138 => 2, 140 => 1, 141 => 1
SELECT COUNT (*) AS count_all, iq AS iq FRA "rekrutterer" GRUPP AV "rekrutter". "IQ" HAR IQ> '134'
Vi kan også blande og matche disse og se for eksempel hvilke kandidater som har IQ høyere enn 140 er bundet opp i relasjoner eller ikke.
Recruit.having ('iq>?', 140) .group (: family_status)
VELG "rekrutter". * FRA "rekrutter" GRUPP AV "rekrutter". "Family_status" HAVING iq> '140'
Å telle disse gruppene er nå altfor lett:
Recruit.having ('iq>?', 140) .group (: family_status) .count # => "gift" => 2, "single" => 1
SELECT COUNT (*) AS count_all, family_status AS family_status FRA "rekrutterer" GROUP BY "rekrutterer". "Family_status" HAVING iq> '140'
Jeg håper dette var et nyttig første blikk på hva Active Record har å tilby for å gjøre forespørselsinnsatsen så lesbar og praktisk som mulig. Samlet sett vil jeg si at det er et utmerket innpakning som holder deg i å skrive SQL for hånd mesteparten av tiden.
I neste artikkel vil vi se nærmere på et par mer involverte finders og utvide det vi lærte så langt.