I den här artikeln kommer du att lära dig grunderna i Active Record-frågor och lära dig några grundläggande uppgifter om SQL på vägen. Det riktar sig till nybörjare som vill börja lära sig mer om databasfrågor i Ruby on Rails.
Aktiv post används för att fråga databasen. Den kan användas med SQL, PostgresSQL och SQLite. För att hämta poster från din databas har du flera sökmetoder till ditt förfogande. Det häftiga med dem är att du kan rädda dig själv för att skriva rå SQL.
Vad gör en sökmetod verkligen? I grund och botten tre saker: dina angivna alternativ omvandlas till en SQL-fråga. Då körs SQL-frågan och hämtar data från databasen. Också för varje rad i resultatlistan får vi nyligen instansierade Ruby-objekt av modellen som motsvarar förfrågan.
Om du inte har spelat med SQL tidigare, ska jag försöka mitt bästa för att hålla saker enkelt och presentera dig för de allra bästa grunderna. Följ SQL-exemplen och försök att förstå dessa enkla frågor. SQL är ingen raketvetenskap verkligen - syntaxen tar bara lite att vänja sig vid. Detta kommer förhoppningsvis göra din aptit att jaga några användbara handledning som fyller i ämnena.
Låt oss ta en titt på några metoder som står till ditt förfogande:
hitta
först
sista
find_by
Allt
find_each
find_in_batches
var
ordning
begränsa
offset
grupp
har
Alla dessa kommer att returnera en instans av Active :: Relation
. En Vad? Det är en klass som är namespaced inom modul ActiveRecord
, och det låter oss ringa flera sökmetoder och kedja dem. Det här objektet är hjärtat av söksyntaxen som används i Rails. Låt oss kolla klassen av ett sådant objekt och se till oss själva:
Agent.where (namn: "James Bond"). Klass # => ActiveRecord :: Relation
hitta
Med den här metoden kan du tillhandahålla det primära idet för ett objekt och hämta det enskilda objektet för dig. Om du tillhandahåller en rad ids kan du också hämta flera objekt.
bond = Agent.find (7)
VÄLJ "agenter". * FRÅN "agenter" VAR "agenter". "Id" =? GRÄNS 1 [["id", 7]]
Denna rad i SQL anger att du vill välja alla (*
) attribut från medel
bord och "filtrera" ut endast posten med id 7. En gräns gör att den bara returnerar en enda post från databasen.
först
, sista
Otroligt nog kommer dessa att ge dig de första och sista posterna som kan identifieras med deras primära nyckel. Den intressanta delen är dock att du kan ge ett valfritt nummer som returnerar dig det första eller sista av det antal poster.
enemy_agents = SpectreAgent.first (10) enemy_agents = SpectreAgent.last (10)
Under huven ger du en ny gräns för det nummer du anger och beställer det stigande eller fallande.
SELECT "spectreagents". * FRÅN "spectreagents" ORDER BY "spectreagents". "Id" ASC LIMIT 10 SELECT "spectreagents". * FRÅN "spectreagents" ORDER BY "spectreagents". "Id" DESC LIMIT 10
find_by
Den här sökaren returnerar det första objektet som matchar det villkor du anger.
bond = Agent.find_by (sista namn: "Bond")
VÄLJ "agenter". * FRÅN "agenter" VAR "agenter". "Last_name" =? GRÄNS 1 [["sista namn", "Bond"]]
Självklart behöver vi ofta iterera över en samling objekt med viss agenda. Att hämta ett enskilt objekt eller ett fåtal få i hand är bra, men oftare vill vi att Active Record ska hämta objekt i satser.
Visar användare alla typer av listor är brödet och smöret för de flesta Rails-appar. Vad vi behöver är ett kraftfullt verktyg med ett bekvämt API för att samla in dessa objekt för oss - förhoppningsvis på ett sätt som låter oss undvika att skriva själva SQL själva själva för det mesta.
Allt
mi6_agents = Agents.all
VÄLJ "agenter". * FRÅN "agenter"
Denna metod är praktisk för relativt små samlingar av objekt. Försök att föreställa dig att göra detta på en samling av alla Twitter-användare. Nej, inte en bra idé. Vad vi vill istället är ett mer finjusterat tillvägagångssätt för större bordstorlekar.
Att hämta hela bordet kommer inte att skala! Varför? Eftersom vi inte bara skulle begära en massa objekt skulle vi också behöva bygga ett objekt per rad i den här tabellen och lägga dem i en array i minnet. Jag hoppas det här låter inte som en bra idé! Så vad är lösningen för detta? Partier! Vi delar upp dessa samlingar i satser som är enklare på minnet för bearbetning. woohoo!
Låt oss ta en titt på find_each
och find_in_batches
. Båda är lika men uppför sig annorlunda i hur de ger föremål till block. De accepterar ett alternativ att reglera batchstorleken. Standardvärdet är 1.000.
find_each
NewRecruit.find_each gör | rekrytera | recruit.start_hellweek slutet
VÄLJ "newrecruits". * FRÅN "newrecruits" BESTÄLL AV "newrecruits". "Id" ASC LIMIT 1000
I det här fallet hämtar vi en standard sats på 1000 nya rekryter, ger dem till blocket och skickar dem till helvete-en efter en. Eftersom satser slicerar upp samlingar kan vi också berätta för var de ska börja via Start
. Låt oss säga att vi vill behandla 3 000 möjliga rekryter på en gång och vill börja med 4 000.
NewRecruit.find_each (start: 4000, batch_size: 3000) gör | rekrytera | recruit.start_hellweek slutet
VÄLJ "newrecruits". * FRÅN "newrecruits" WHERE ("newrecruits". "Id"> = 4000) BESTÄLL AV "newrecruits". "Id" ASC LIMIT 3000
För att upprepa först hämtar vi en serie 3 000 Ruby-objekt och skickar dem sedan in i blocket. Start
Låt oss ange ID-nummer för poster där vi vill börja hämta denna sats.
find_in_batches
Den här ger sitt parti som en matris till blocket - den överför den till ett annat objekt som föredrar att hantera samlingar. SQL är densamma här.
NewRecruit.find_in_batches (start: 2700, batch_size: 1350) gör | rekryter | field_kitchen.prepare_food (rekryterar) slut
var
Vi måste gå över var
innan vi fortsätter vidare. Det här låter oss ange villkor som begränsar antalet poster som returneras av våra frågor - ett filter för "var" för att hämta poster i databasen. Om du har spelat med SQL VAR
klausuler då kanske du bara känner dig som hemma - samma sak med den här Ruby wrappen.
I SQL tillåter vi oss att ange vilken tabellrad vi vill påverka, i grunden där den uppfyller något slags kriterier. Detta är en valfri klausul förresten. I rå SQL nedan väljer vi endast rekryter som är föräldralösa via VAR
.
Välj en specifik rad från en tabell.
VÄLJ * FRÅN rekryterare WHERE FamilyStatus = 'Orphan';
Via var
, Du kan ange villkor med strängar, haschar eller arrays. Genom att lägga allt detta tillsammans låter Active Record dig filtrera efter sådana förhållanden som denna:
promising_candidates = Recruit.where ("family_status = 'orphan'")
VÄLJ "rekryter". * FRÅN "rekryter" VAR (family_status = 'orphan')
Ganska snyggt, eller hur? Jag vill nämna att detta fortfarande är en funktionsövning - vi anger bara hur vi vill filtrera denna lista genast. Från listan över alla rekryter kommer detta att returnera en filtrerad lista över föräldralösa kandidater. Detta exempel är ett strängtillstånd. Håll dig borta från rena strängförhållanden eftersom de inte anses vara säkra på grund av deras sårbarhet mot SQL-injektioner.
I exemplet ovan sätter vi föräldralös
variabel i strängen med villkoren. Detta anses vara en dålig övning eftersom den är osäker. Vi måste undvika variabeln för att undvika denna säkerhetsproblem. Du borde läsa om SQL-injektion om det här är helt nytt för dig - din databas kan bero på den.
promising_candidates = Recruit.where ("family_status =?", "orphan" ")
De ?
kommer att ersättas som tillståndsvärdet med nästa värde i argumentlistan. Så frågetecknet är en platshållare i grund och botten. Du kan också ange flera villkor med flera ?
och kedja dem tillsammans. I ett verkligt scenario skulle vi använda en params hash så här:
promising_candidates = Recruit.where ("family_status =?", params [: rekryterar])
Om du har ett stort antal variabla förhållanden, bör du använda nyckel / värde platshållarvillkor.
promising_candidates = Recruit.where ("family_status =: preferred_status OCH iq> =: required_iq OCH charming =: lady_killer", preferred_status: "orphan", required_iq: 140, lady_killer: true)
VÄLJ "rekryter". * FRÅN "rekryter" VAR (family_status = 'orphan' OCH iq> = 140 OCH lady_killer = true)
Exemplet ovan är naturligtvis dumt, men det visar tydligt fördelarna med platshållarens notering. Hash notationen, i allmänhet, är definitivt den mer läsbara.
promising_candidates = Recruit.where (family_status: 'orphan') promising_candidates = Recruit.where ("charming": true)
Som du kan se kan du gå med symboler eller strängar upp till dig. Låt oss stänga detta avsnitt med intervall och negativa villkor via NOT.
promising_candidates = Recruit.where (födelsedag: ('1994-01-01' ... '2000-01-01'))
Två prickar och du kan upprätta vilket sortiment du behöver.
promising_candidates = Recruit.where.not (tecken: "coward")
Du kan klämma på inte
på var
att filtrera bort alla fånder och få bara resultat som inte har den specifika, oönskade attributet. Under huven, a !=
negerar WHERE "filter".
VÄLJ "rekryter". * FRÅN "rekryter" VAR ("rekryterar". "Karaktär"! =?) [["Karaktär", "feg"]]
ordning
För att inte dra dig ihjäl med det, låt oss göra det här snabbt.
kandidater = Recruit.order (: date_of_birth)
kandidater = Recruit.order (: date_of_birth,: desc)
Tillämpa : asc
eller : desc
för att sortera det i enlighet därmed. Det är i princip det, så låt oss gå vidare!
begränsa
Du kan minska antalet returnerade poster till ett visst nummer. Som tidigare nämnts behöver du inte alla poster återkomma. Exemplet nedan ger dig de fem första rekryterna i databasen - de första fem idsna.
five_candidates = Recruit.limit (5)
VÄLJ "rekryter". * FRÅN "rekryter" LIMIT 5
offset
Om du någonsin undrat hur pagination fungerar under huven, begränsa
och offset
-ihop-gör det hårda arbetet. begränsa
kan stå på egen hand, men offset
beror på den tidigare.
Att ställa in en förskjutning är mest användbar för pagination och låter dig hoppa över önskat antal rader i databasen. Sidan två av en lista över kandidater kan ses upp så här:
Recruit.limit (20) .offset (20)
SQL ser ut så här:
VÄLJ "rekryter". * FRÅN "rekryter" LIMIT 20 OFFSET 20
Återigen väljer vi alla kolumner från Rekrytera
databasmodell, vilket begränsar posterna tillbaka till 20 Ruby-objekt av Class Recruit och hoppar över de första 20.
Låt oss säga att vi vill ha en lista över rekryter som grupperas av deras IQ. I SQL kan detta se ut så här.
VÄLJ "rekryter". * FRÅN "rekryter" GRUPP AV "rekryter". "IQ"
Detta skulle få dig en lista där du kan se vilka möjliga rekryter som har en IQ av låt oss säga 120, och sedan en annan grupp med 140, och så vidare-vad deras IQ är och hur många skulle falla under ett visst nummer. Så när två rekryter har samma IQ på 130, skulle de grupperas ihop.
En annan lista kan grupperas av möjliga kandidater som lider av klaustrofobi, rädsla för höjder eller som inte är medicinskt lämpade för dykning. Active Record-frågan skulle helt enkelt se ut så här:
grupp
Candidate.group (: iq)
När vi räknar antalet kandidater får vi tillbaka en mycket användbar hash.
Candidate.group (: iq) .count # => 130 => 7, 134 => 4, 135 => 3, 138 => 2, 140 => 1, 141 => 1
Där går vi - vi fick sju möjliga rekryter med en IQ på 130 och endast en med 141. Den resulterande SQL skulle se ut så här:
VÄLJ COUNT (*) AS count_all, iq AS iq FRÅN "kandidater" GRUPP AV "kandidater". "IQ"
Den viktiga delen är GRUPP AV
del. Som ni kan se använder vi kandidatbordet för att få sina ids. Vad du också kan observera från det här enkla exemplet är hur mycket mer bekvämt de aktiva versionerna läser och skriver. Tänk dig att göra detta för hand på mer extravaganta exempel. Visst, ibland måste du, men hela tiden är det klart en smärta som vi med glädje kan undvika.
har
Vi kan ange denna grupp ännu mer genom att använda HAR
-typ av ett filter för grupp
. I det avseendet, har
är en sorts VAR
klausul för GRUPP
. Med andra ord, har
är beroende av att använda grupp
.
Recruit.having ('iq>?', 134) .grupp (: iq)
VÄLJ "rekryter". * FRÅN "rekryter" GRUPP AV "rekryterar". "IQ" HAR IQ> '134'
Vi har nu grupperat våra kandidater i listor över personer som har en lägsta IQ på 135. Låt oss räkna dem för att få lite statistik:
Recruit.having ('iq>?', 134) .group (: iq) .count # => 135 => 3, 138 => 2, 140 => 1, 141 => 1
VÄLJ COUNT (*) AS count_all, iq AS iq FRÅN "rekryterar" GRUPP BY "rekryterar". "IQ" HAR IQ> '134'
Vi kan också blanda och matcha dessa och se till exempel vilka kandidater som har IQ högre än 140 är bundna i relationer eller inte.
Recruit.having ('iq>?', 140) .grupp (: family_status)
VÄLJ "rekryter". * FRÅN "rekryter" GRUPP AV "rekryter". "Family_status" HAR IQ> '140'
Att räkna dessa grupper är nu alltför lätt:
Recruit.having ('iq>?', 140) .grupp (: family_status) .count # => "married" => 2, "single" => 1
VÄLJ COUNT (*) AS count_all, family_status AS family_status FRÅN "rekryter" GROUP BY "rekryterar". "Family_status" HAR IQ> '140'
Jag hoppas att det här var en användbar första titt på vad Active Record har att erbjuda för att göra dina frågeinsatser så läsliga och bekväma som möjligt. Sammantaget skulle jag säga att det är ett utmärkt inslag som hindrar dig från att skriva SQL för hand mesteparten av tiden.
I nästa artikel tittar vi på ett par mer involverade finders och utökar vad vi hittills lärt oss.