Frågor i Rails, Del 1

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.

ämnen

  • Enstaka objekt
  • Flera objekt
  • Betingelser
  • Beställning
  • gränser
  • Grupp & Med

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:

rails

Agent.where (namn: "James Bond"). Klass # => ActiveRecord :: Relation

Enstaka objekt

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

rails

bond = Agent.find (7)

SQL

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. 

rails

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.

SQL

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.

rails

bond = Agent.find_by (sista namn: "Bond")

SQL

VÄLJ "agenter". * FRÅN "agenter" VAR "agenter". "Last_name" =? GRÄNS 1 [["sista namn", "Bond"]]

Flera objekt

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

rails

mi6_agents = Agents.all

SQL

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

rails

NewRecruit.find_each gör | rekrytera | recruit.start_hellweek slutet

SQL

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.

rails

NewRecruit.find_each (start: 4000, batch_size: 3000) gör | rekrytera | recruit.start_hellweek slutet

SQL

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.

rails

NewRecruit.find_in_batches (start: 2700, batch_size: 1350) gör | rekryter | field_kitchen.prepare_food (rekryterar) slut

Betingelser

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

SQL

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:

rails

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

SQL

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.

Argumentsäkerhet

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.

rails

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.

rails

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)

SQL

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.

rails

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.

rails

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å intevar att filtrera bort alla fånder och få bara resultat som inte har den specifika, oönskade attributet. Under huven, a != negerar WHERE "filter".

SQL

VÄLJ "rekryter". * FRÅN "rekryter" VAR ("rekryterar". "Karaktär"! =?) [["Karaktär", "feg"]]

Beställning

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

gränser

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

rails

five_candidates = Recruit.limit (5) 

SQL

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:

rails

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.

Grupp & Med

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

rails

Candidate.group (: iq)

När vi räknar antalet kandidater får vi tillbaka en mycket användbar hash.

rails

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:

SQL

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.

rails

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

SQL

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:

rails

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

SQL

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. 

rails

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

SQL

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:

rails

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

SQL

VÄLJ COUNT (*) AS count_all, family_status AS family_status FRÅN "rekryter" GROUP BY "rekryterar". "Family_status" HAR IQ> '140'

Slutgiltiga tankar

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.