Ydeevne er et af de vigtigste aspekter at tage hensyn til, når man udvikler webapplikationer. En analyse af, hvordan data hentes fra en database, er et godt udgangspunkt for at finde forbedringer. I denne artikel finder du eksempler på, hvordan du kan forbedre ydeevnen ved at bruge aggregerede funktioner og filtrere data på databaseniveau.
Lidt kontekst til at begynde med
Denne artikel er inspireret af et virkeligt problem, som jeg engang blev stillet over for. Jeg lærte meget af at håndtere det, og jeg har det stadig som en reference i mit hoved. Jeg synes, at eksempler er en god læringsressource, de kan afklare meget. I denne artikel vil jeg gerne dele nogle eksempler på brugen af Active Record-forespørgselsmetoderne med dig.
For ikke at introducere domænespecifikke detaljer vil jeg bruge et eksempel på en applikation til et bibliotek for at illustrere eksemplerne. Det hele er ret enkelt, som vist i diagrammet nedenfor. Vi har fire tabeller: forfattere, bøger, brugere og udlejning. En bruger kan låne mange bøger, og en bog kan lånes af mange brugere, så vi har brug for en sammenføjningstabel til at modellere mange-til-mange-relationer. Det er udlejningstabellen i vores tilfælde. Vi gemmer også nogle ekstra oplysninger der, som er datoerne for lån og aflevering. En forfatter kan have mange bøger tilknyttet sit navn. En bog har også en attribut, der definerer dens genre.
Statistik over brugernes læsning
Opgaven var at udarbejde statistik for en enkelt bruger, så vi kunne se, hvor mange bøger fra hver genre der var lånt. Min første tanke var at hente alle bøger, som er blevet lånt af brugeren, gruppere dem efter deres genre og derefter lave mappingen, så hver genre får tildelt et antal bøger i stedet for en liste. Her er, hvad jeg kom frem til:
Selv om denne tilgang fungerer og ser pæn ud, udnytter den ikke alle de muligheder, som Active Record-forespørgselsmetoderne tilbyder. Takket være dem er vi i stand til at filtrere og aggregere data på databaseniveau uden at bruge rå SQL direkte i vores Kode. At arbejde på db-niveau øger også vores effektivitet.
I eksemplet ovenfor kan vi bruge gruppemetoden i stedet for Rubys gruppeefter metode. Den vil anvende GROUPBY-klausul til tSQL-forespørgslen. Desuden kan kortlægnings- og størrelsesmetoden erstattes med en tælleaggregeringsfunktion. I sidste ende står vi tilbage med en forespørgsel, der ser sådan ud:
Book.joins(:rentals).where(rentals: { user: user }).group(:genre).count(:books)
Det ser endnu mere enkelt ud!
Andre nyttige eksempler
Nedenfor finder du nogle andre måder at bruge forespørgselsmetoderne på, som jeg synes er værd at kende.
Invitation til inaktive brugere
OPGAVE: Filtrer brugere, der aldrig har lånt en bog eller har gjort det for mere end et år siden.
Vi kan hente alle brugere ved at inkludere de tilknyttede udlejninger og derefter filtrere dem ved hjælp af select-metoden.
User.includes(:rentals).select do |user|
user.rentals.empty? || user.rentals.none? { |rental| rental.start_date >= Date.today - 1.year }
end
Men der er selvfølgelig ingen grund til at hente alt. Ved at bruge forespørgselsmetoderne kan vi filtrere det ud på db-niveau. Lad os først vælge brugere, som har lånt nogle bøger det seneste år, og derefter udelukke dem fra det endelige udvalg.
OPGAVE: Find forfattere med en eller nul lånte bøger
Det er superenkelt at gøre det med select-metoden, men igen - der er ingen grund til at arbejde med så stort et datasæt, da db'en kan filtrere det for os:
Det er vigtigt at huske én ting, når man bruger left_joins (og outer joins generelt). Hvis der er poster i den venstre tabel (her: forfattere), som ikke har tilsvarende poster i den højre tabel (her: bøger), så vil kolonnerne i den højre tabel blive udfyldt med nul-værdier.
Da vi også har brug for forfattere med en bog tildelt i systemet, er der nogle flere operationer, der skal udføres. Vi skal gruppere, tælle og tilføje en betingelse. Her kan du se, hvordan vi sætter det hele sammen:
Betingelsen kommer efter aggregeringsfunktionen, så vi er nødt til at bruge HAVING-sætningen i stedet for WHERE-sætningen til at specificere den.
Active Record-forespørgselsmetoderne er værd at tjekke, når man tænker på applikationens ydeevne. De kan forenkle din kode og få den til at fungere hurtigere. Jeg håber, at de delte eksempler vil hjælpe dig med at udforske de muligheder, forespørgselsmetoderne har at byde på.