Query's in Rails, deel 1

In dit artikel leert u de basisprincipes van Active Record-query's en leert u onderweg enkele basisbeginselen over SQL. Het is bedoeld voor beginners die meer willen leren over databasequery's in Ruby on Rails.

Onderwerpen

  • Enkele objecten
  • Meerdere objecten
  • Voorwaarden
  • Bestellen
  • grenzen
  • Groep en hebben

Active Record wordt gebruikt voor het opvragen van de database. Het kan worden gebruikt met SQL, PostgresSQL en SQLite. Voor het ophalen van records uit uw database beschikt u over verschillende zoekmethoden. Het leuke eraan is dat je jezelf de moeite van het schrijven van onbewerkte SQL kunt besparen. 

Wat doet een vindermethode echt? In principe drie dingen: uw aangeboden opties worden geconverteerd naar een SQL-query. Vervolgens wordt de SQL-query uitgevoerd en worden gegevens uit de database opgehaald. Voor elke rij in die resultatenlijst krijgen we ook nieuwe instantine Ruby-objecten van het model die overeenkomen met de query. 

Als je nog niet eerder met SQL hebt gespeeld, zal ik mijn best doen om de dingen eenvoudig te houden en je kennis te laten maken met de basis. Volg de SQL-voorbeelden en probeer betekenis te geven aan deze eenvoudige query's. SQL is echt geen echte raketwetenschap, de syntaxis is even wennen. Dit zal hopelijk je eetlust opwekken om een ​​aantal nuttige tutorials op te sporen die de lege plekken vullen. 

Laten we een paar methoden bekijken die tot uw beschikking staan:

  • vind
  • eerste
  • laatste
  • find_by
  • allemaal
  • find_each
  • find_in_batches
  • waar
  • bestellen
  • begrenzing
  • compenseren
  • groep
  • met

Al deze keren een exemplaar terug van ActiveRecord :: Relation. Een wat? Het is een klasse die is namespaceed binnen de module ActiveRecord, en het stelt ons in staat om meerdere zoekmethoden aan te roepen en te ketenen. Dit object is het hart van de query-syntaxis die wordt gebruikt in Rails. Laten we de klasse van een dergelijk object bekijken en zelf zien:

rails

Agent.where (naam: 'James Bond'). Class # => ActiveRecord :: Relation

Enkele objecten

  • vind

Met deze methode kunt u de primaire id van een object invoeren en dat ene object voor u ophalen. Als u een array met id's opgeeft, kunt u ook meerdere objecten ophalen.

rails

bond = Agent.find (7)

SQL

SELECTEER "agents". * FROM "agents" WAAR "agents". "Id" =? LIMIET 1 [["id", 7]]

In deze SQL-regel staat dat u alles wilt selecteren (*) attributen van de agenten tabel en "filter" alleen de record met de id 7. Een limiet zorgt ervoor dat het slechts één record uit de database retourneert.

  • eerste, laatste

Niet verwonderlijk, deze geven u de eerste en laatste records die kunnen worden geïdentificeerd aan de hand van hun primaire sleutel. Het interessante deel is echter dat u een optioneel nummer kunt opgeven dat u het eerste of laatste van dat aantal records oplevert. 

rails

enemy_agents = SpectreAgent.first (10) enemy_agents = SpectreAgent.last (10)

Onder de motorkap biedt u een nieuwe limiet voor het nummer dat u opgeeft en het oplopend of aflopend te bestellen.

SQL

SELECT "spectreagents". * FROM "spectreagents" ORDER BY "spectreagents". "Id" ASC LIMIT 10 SELECT "spectreagents". * FROM "spectreagents" ORDER BY "spectreagents". "Id" DESC LIMIT 10
  • find_by

Deze vinder retourneert het eerste object dat overeenkomt met de voorwaarde die u opgeeft.

rails

bond = Agent.find_by (achternaam: 'Bond')

SQL

SELECTEER "agents". * FROM "agents" WAAR "agents". "Last_name" =? LIMIT 1 [["achternaam", "Obligatie"]]

Meerdere objecten

Het is duidelijk dat we vaak een verzameling objecten met een bepaalde agenda moeten herhalen. Het ophalen van een enkel object of een paar geselecteerde met de hand is leuk, maar vaker wel dan niet, we willen dat Active Record objecten in batches ophaalt. 

Gebruikers van allerlei soorten lijsten tonen is het brood en boter voor de meeste Rails-apps. Wat we nodig hebben, is een krachtige tool met een handige API om deze objecten voor ons te verzamelen - hopelijk op een manier die ons voorkomt om de betrokken SQL zelf meestal te schrijven.

  • allemaal

rails

mi6_agents = Agents.all

SQL

SELECTEER "agenten". * VAN "agenten"

Deze methode is handig voor relatief kleine verzamelingen objecten. Probeer je voor te stellen dat je dit doet op een verzameling van alle Twitter-gebruikers. Nee, geen goed idee. Wat we willen is een meer verfijnde aanpak voor grotere tafelformaten. 

Het ophalen van de hele tabel gaat niet op schaal! Waarom? Omdat we niet alleen om een ​​heleboel objecten zouden vragen, zouden we ook één object per rij in deze tabel moeten bouwen en ze in een array in het geheugen moeten plaatsen. Ik hoop dat dit niet als een goed idee klinkt! Dus wat is de oplossing hiervoor? Batches! We verdelen deze collecties in batches die gemakkelijker te verwerken zijn. Woohoo!

Laten we eens kijken find_each en find_in_batches. Beide zijn vergelijkbaar maar gedragen zich anders in hoe ze objecten in blokken opbrengen. Ze accepteren een optie om de batchgrootte te regelen. De standaardinstelling is 1.000.

  • find_each

rails

NewRecruit.find_each do | recruit | recruit.start_hellweek end

SQL

SELECTEER "newrecruits". * FROM "newrecruits" BESTELLING BY "newrecruits". "Id" ASC LIMIT 1000

In dit geval halen we een standaardbatch van 1.000 nieuwe rekruten op, brengen ze naar het blok en sturen ze week voor week naar de hel. Omdat batches collecties opdelen, kunnen we ze ook vertellen waar ze moeten beginnen begin. Laten we zeggen dat we 3.000 mogelijke rekruten in één keer willen verwerken en willen starten bij 4.000.

rails

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

SQL

SELECTEER "newrecruits". * FROM "newrecruits" WHERE ("newrecruits". "Id"> = 4000) BESTELLING OP "newrecruits". "Id" ASC LIMIT 3000

Om te herhalen, halen we eerst een batch van 3.000 Ruby-objecten op en sturen ze vervolgens naar het blok. begin laat ons de ID van de records specificeren waar we willen beginnen met het ophalen van deze batch.

  • find_in_batches

Deze geeft zijn batch als een array aan het blok door - deze geeft het door aan een ander object dat de voorkeur geeft aan het verwerken van verzamelingen. De SQL is hier hetzelfde.

rails

NewRecruit.find_in_batches (start: 2700, batch_size: 1350) do | rekruten | field_kitchen.prepare_food (rekruten) einde

Voorwaarden

  • waar

We moeten overgaan waar voordat we verder gaan. Hiermee kunnen we voorwaarden opgeven die het aantal records beperken dat door onze query's wordt geretourneerd, een filter voor "where" om records uit de database op te halen. Als je met SQL hebt gespeeld WAAR clausules dan kun je je gewoon meteen thuis voelen - hetzelfde met deze Ruby wikkel. 

In SQL stelt dit ons in staat te specificeren welke tabelrij we willen beïnvloeden, in principe waar het aan een of ander criterium voldoet. Dit is trouwens een optionele clausule. In de onbewerkte SQL hieronder selecteren we alleen rekruten die wees zijn via WAAR

Selecteer een specifieke rij uit een tabel.

SQL

SELECTEER * VAN Recruits WHERE FamilyStatus = 'Orphan';

Via waar, u kunt voorwaarden opgeven met tekenreeksen, hashes of arrays. Door dit alles samen te voegen, kunt u met Active Record op dergelijke omstandigheden filteren:

rails

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

SQL

SELECTEER "rekruten". * VAN "rekruten" WAAR (family_status = 'wees')

Best leuk, toch? Ik wil vermelden dat dit nog steeds een zoekbewerking is: we specificeren alleen hoe we deze lijst meteen willen filteren. Van de lijst met alle rekruten zal dit een gefilterde lijst van verweesde kandidaten retourneren. Dit voorbeeld is een stringvoorwaarde. Blijf uit de buurt van pure string-voorwaarden, omdat ze niet als veilig worden beschouwd vanwege hun kwetsbaarheid voor SQL-injecties.

Argumentveiligheid

In het bovenstaande voorbeeld plaatsen we de wees- verander in de string met de voorwaarden. Dit wordt als een slechte praktijk beschouwd omdat het onveilig is. We moeten aan de variabele ontsnappen om deze beveiligingskwetsbaarheid te voorkomen. U moet de SQL-injectie lezen als dit totaal nieuws voor u is - uw database kan ervan afhankelijk zijn.

rails

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

De ? wordt als voorwaarde vervangen door de volgende waarde in de lijst met argumenten. Dus het vraagteken is eigenlijk een placeholder. U kunt ook meerdere voorwaarden opgeven met meerdere ? en keten ze samen. In een real-life scenario gebruiken we een hash van params als deze:

promising_candidates = Recruit.where ("family_status =?", params [: rekruten])

Als u een groot aantal variabele voorwaarden hebt, moet u de tijdelijke aanduidingen voor sleutel / waarde gebruiken.

rails

promising_candidates = Recruit.where ("family_status =: preferred_status EN iq> =: required_iq AND charming =: lady_killer", preferred_status: 'orphan', required_iq: 140, lady_killer: true)

SQL

SELECTEER "rekruten". * VAN "rekruten" WAAR (family_status = 'wees' EN iq> = 140 EN lady_killer = true)

Het bovenstaande voorbeeld is natuurlijk gek, maar het toont duidelijk de voordelen van de plaatshoudernotatie. De hashnotatie is over het algemeen beslist leesbaarder.

rails

promising_candidates = Recruit.where (family_status: 'orphan') promising_candidates = Recruit.where ('charmant': waar)

Zoals je kunt zien, kun je met symbolen of touwtjes naar je toe gaan. Laten we dit gedeelte met bereiken en negatieve condities sluiten via NOT.

rails

promising_candidates = Recruit.where (verjaardag: ('1994-01-01' ... '2000-01-01'))

Twee punten en je kunt elk bereik instellen dat je nodig hebt.

promising_candidates = Recruit.where.not (karakter: 'lafaard')

Je kunt de niet op de waar om alle lafaards eruit te filteren en alleen resultaten te krijgen die niet dat specifieke, ongewenste kenmerk hebben. Onder de motorkap, een != ontkent het WHERE "filter".

SQL

SELECTEER "rekruten". * VAN "rekruten" WAAR ("rekruten". "Teken"! =?) [["Karakter", "lafaard"]]

Bestellen

  • bestellen

Om je er niet dood mee te vervelen, laten we dit snel doen.

kandidaten = Recruit.order (: date_of_birth)
kandidaten = Recruit.order (: date_of_birth,: desc)

Van toepassing zijn : ASC of : desc om het dienovereenkomstig te sorteren. Dat is het eigenlijk, dus laten we doorgaan!

grenzen

  • begrenzing

U kunt het aantal geretourneerde records reduceren tot een bepaald aantal. Zoals eerder vermeld, zult u meestal niet alle geretourneerde gegevens nodig hebben. Het onderstaande voorbeeld geeft u de eerste vijf rekruten in de database - de eerste vijf id's.

rails

five_candidates = Recruit.limit (5) 

SQL

SELECTEER "rekruten". * VAN "rekruten" LIMIET 5
  • compenseren

Als je je ooit hebt afgevraagd hoe paginering werkt onder de motorkap, begrenzing en compenseren-in combinatie - doe het harde werk. begrenzing kan op zichzelf staan, maar compenseren hangt af van de eerste.

Het instellen van een offset is vooral handig voor paginering en laat u het gewenste aantal rijen in de database overslaan. Pagina twee van een kandidatenlijst kan als volgt worden opgezocht:

rails

Recruit.limit (20) .offset (20)

De SQL ziet er als volgt uit:

SELECTEER "rekruten". * VAN "rekruten" LIMIET 20 OFFSET 20

Nogmaals, we selecteren alle kolommen van de Rekruut database model, beperkend de records geretourneerd naar 20 Ruby-objecten van Class Recruit en spring over de eerste 20.

Groep en hebben

Laten we zeggen dat we een lijst van rekruten willen die gegroepeerd zijn op hun IQ's. In SQL zou dit er ongeveer zo uit kunnen zien.

SELECTEER "rekruten". * VAN "rekruten" GROEP VAN "rekruten". "Iq"

Zo krijg je een lijst waarin je ziet welke mogelijke rekruten een IQ hebben van laten we zeggen 120, en dan een andere groep van 140, enzovoort, wat hun IQ's ook zijn en hoeveel er onder een specifiek nummer vallen. Dus wanneer twee rekruten hetzelfde IQ van 130 hebben, zouden ze bij elkaar worden gegroepeerd. 

Een andere lijst zou gegroepeerd kunnen zijn door mogelijke kandidaten die last hebben van claustrofobie, hoogtevrees of die medisch niet geschikt zijn om te duiken. De Active Record-query zou er eenvoudig als volgt uitzien:

  • groep

rails

Candidate.group (: iq)

Als we het aantal kandidaten tellen, krijgen we een zeer nuttige hash terug.

rails

Candidate.group (: iq) .count # => 130 => 7, 134 => 4, 135 => 3, 138 => 2, 140 => 1, 141 => 1

Daar gaan we - we hebben zeven mogelijke rekruten met een IQ van 130 en slechts één met 141. De resulterende SQL ziet er als volgt uit:

SQL

SELECTEER COUNT (*) AS count_all, iq AS iq VAN "kandidaten" GROUP BY "kandidaten". "Iq"

Het belangrijke stuk is het GROEP DOOR een deel. Zoals u kunt zien, gebruiken we de kandidaten-tabel om hun id's te krijgen. Wat u ook uit dit eenvoudige voorbeeld kunt opmerken, is hoeveel handiger de Active Record-versies lezen en schrijven. Stel je voor dat je dit met de hand doet aan meer extravagante voorbeelden. Natuurlijk, soms moet dat, maar de hele tijd is duidelijk een pijn die we graag vermijden.

  • met

We kunnen deze groep nog meer specificeren door te gebruiken HEBBEN-een soort filter voor de groep. Op die manier, met is een soort van WAAR clausule voor GROEP. Met andere woorden, met is afhankelijk van het gebruik groep.

rails

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

SQL

SELECTEER "rekruten". * VAN "rekruten" GROEP DOOR "rekruten". "Iq" MET iq> '134'

We hebben nu onze kandidaten gegroepeerd in lijsten met mensen met een minimum-IQ van 135. Laten we ze tellen om een ​​aantal statistieken te krijgen:

rails

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

SQL

SELECTEER COUNT (*) AS count_all, iq AS iq FROM "recruits" GROUP BY "rekruten". "Iq" HAVING iq> '134'

We kunnen deze ook mixen en matchen en zien bijvoorbeeld welke kandidaten met IQ's hoger dan 140 vastzitten in relaties of niet. 

rails

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

SQL

SELECTEER "rekruten". * VAN "rekruten" GROEP DOOR "rekruten". "Family_status" MET iq> '140'

Het tellen van deze groepen is nu allemaal te gemakkelijk:

rails

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

SQL

SELECTEER COUNT (*) AS count_all, family_status AS family_status FROM "recruits" GROUP BY "rekruten". "Family_status" HAVING iq> '140'

Laatste gedachten

Ik hoop dat dit een nuttige eerste blik was op wat Active Record te bieden heeft om uw zoekopdracht zo leesbaar en gemakkelijk mogelijk te maken. Over het algemeen zou ik zeggen dat het een uitstekende wrapper is die je ervan weerhoudt om meestal met de hand SQL te schrijven. 

In het volgende artikel zullen we een paar meer betrokken vinders bekijken en verder gaan met wat we tot nu toe hebben geleerd.