Spørsmål i Rails, Del 1

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.

emner

  • Enkeltobjekter
  • Flere objekter
  • Forhold
  • bestilling
  • grenser
  • Gruppe og ha

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:

rails

Agent.where (navn: 'James Bond'). Klasse # => ActiveRecord :: Relasjon

Enkeltobjekter

  • 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.

rails

bond = Agent.find (7)

SQL

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. 

rails

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.

SQL

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.

rails

obligasjon = Agent.find_by (sistenavn: 'Bond')

SQL

VELG "agenter". * FRA "agenter" hvor "agenter". "Last_name" =? LIMIT 1 [["siste navn", "Bond"]]

Flere objekter

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

rails

mi6_agents = Agents.all

SQL

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

rails

NewRecruit.find_each do | rekruttere | recruit.start_hellweek end

SQL

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.

rails

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

SQL

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.

rails

NewRecruit.find_in_batches (start: 2700, batch_size: 1350) rekruttere | field_kitchen.prepare_food (rekrutter) slutten

Forhold

  • 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.

SQL

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:

rails

promising_candidates = Recruit.where ("family_status = 'foreldreløs'")

SQL

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.

Argument Sikkerhet

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.

rails

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.

rails

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)

SQL

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.

rails

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.

rails

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 ikkehvor å filtrere ut alle fiender og få bare resultater som ikke har det spesifikke, uønskede attributtet. Under hetten, a != negerer WHERE "filter".

SQL

SELECT "rekrutter". * FRA "rekrutter" hvor ("rekrutterer". "Karakter"! =?) [["Karakter", "fei"]]

bestilling

  • 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!

grenser

  • 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.

rails

five_candidates = Recruit.limit (5) 

SQL

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:

rails

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.

Gruppe og ha

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

rails

Candidate.group (: iq)

Når vi teller antall kandidater, får vi tilbake en veldig nyttig hash.

rails

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:

SQL

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.

rails

Rekruttere.having ('iq>?', 134) .gruppe (: iq)

SQL

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:

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 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. 

rails

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

SQL

VELG "rekrutter". * FRA "rekrutter" GRUPP AV "rekrutter". "Family_status" HAVING iq> '140'

Å telle disse gruppene er nå altfor lett:

rails

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

SQL

SELECT COUNT (*) AS count_all, family_status AS family_status FRA "rekrutterer" GROUP BY "rekrutterer". "Family_status" HAVING iq> '140'

Siste tanker

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.