SQL Order of Operations – I hvilken rækkefølge MySQL udfører forespørgsler?

Optimer din database, auto-magisk.
Klik for at starte, gratis.

Kendskab til bits og bytes af en SQL-forespørgsels rækkefølge af operationer kan være meget værdifuldt, da det kan lette processen med at skrive nye forespørgsler, samtidig med at det også er meget gavnligt, når du forsøger at optimere en SQL-forespørgsel.

Hvis du leder efter den korte version, er dette den logiske rækkefølge af operationer, også kendt som rækkefølgen af udførelsen, for en SQL-forespørgsel:

  1. FRA, herunder JOINs
  2. WHERE
  3. GROUP BY
  4. HAVING
  5. WINDOW-funktioner
  6. SELECT
  7. DISTINCT
  8. UNION
  9. ORDER BY
  10. LIMIT og OFFSET

Men virkeligheden er ikke så let eller ligetil. Som vi sagde, definerer SQL-standarden rækkefølgen for udførelsen af de forskellige SQL-forespørgselsklausuler. Når det er sagt, udfordrer moderne databaser allerede denne standardorden ved at anvende nogle optimeringstricks, som kan ændre den faktiske udførelsesorden, selv om de skal ende med at returnere det samme resultat, som hvis de kørte forespørgslen i standardudførelsesordenen.

Hvorfor skulle de gøre det? Jo, det kan være fjollet, hvis databasen først ville hente alle de data, der er nævnt i FROM-klausulen (herunder JOIN’erne), før den kigger på WHERE-klausulen og dens indekser. Disse tabeller kan indeholde mange data, så du kan forestille dig, hvad der vil ske, hvis databasens optimizer ville holde sig til den traditionelle rækkefølge af operationer i en SQL-forespørgsel.

Lad os se på hver af SQL-forespørgselsdelene i henhold til deres udførelsesrækkefølge.

FROM og JOINs

De tabeller, der er angivet i FROM-klausulen (herunder JOINs), vil blive evalueret først for at bestemme hele det arbejdssæt, der er relevant for forespørgslen. Databasen samler dataene fra alle tabellerne i henhold til JOINs ON-klausulerne, mens den også henter data fra underafspørgslerne, og den opretter måske endda nogle midlertidige tabeller til at opbevare de data, der returneres fra underafspørgslerne i denne klausul.

I mange tilfælde vil databasens optimizer dog vælge at evaluere WHERE-delen først, for at se hvilken del af arbejdssættet der kan udelades (helst ved hjælp af indekser), så den ikke vil puste datasættet for meget op, hvis det ikke er nødvendigt.

Where-klausulen

Herre-klausulen vil være den anden der evalueres, efter FROM-klausulen. Vi har det fungerende datasæt på plads, og nu kan vi filtrere dataene i henhold til betingelserne i WHERE-klausulen.

Disse betingelser kan indeholde henvisninger til data og tabeller fra FROM-klausulen, men kan ikke indeholde henvisninger til aliaser, der er defineret i SELECT-klausulen, da disse data og disse aliaser måske endnu ikke “eksisterer” i denne sammenhæng, da denne klausul endnu ikke er blevet evalueret af databasen.

En almindelig faldgrube for WHERE-klausulen ville også være at forsøge at filtrere aggregerede værdier fra i WHERE-klausulen, f.eks. med denne klausul: “WHERE sum(available_stock) > 0”. Denne erklæring vil mislykkes ved udførelsen af forespørgslen, fordi aggregeringer vil blive evalueret senere i processen (se afsnittet GROUP BY nedenfor). Hvis du vil anvende en filtreringsbetingelse på aggregerede data, skal du bruge HAVING-klausulen og ikke WHERE-klausulen.

GROUP BY-klausulen

Nu, hvor vi har filtreret datasættet ved hjælp af WHERE-klausulen, kan vi aggregere dataene i henhold til en eller flere kolonner, der er angivet i GROUP BY-klausulen. Gruppering af dataene er faktisk en opdeling af dem i forskellige dele eller spande, hvor hver spand har en nøgle og en liste over rækker, der matcher denne nøgle. Hvis du ikke har en GROUP BY-klausul, svarer det til at putte alle rækker i én stor spand.

Når du har aggregeret dataene, kan du nu bruge aggregeringsfunktioner til at returnere en værdi pr. gruppe for hver af spandene. Sådanne aggregeringsfunktioner omfatter COUNT, MIN, MAX, SUM og andre.

HAVING-klausul

Nu, hvor vi har grupperet dataene ved hjælp af GROUP BY-klausulen, kan vi bruge HAVING-klausulen til at filtrere nogle spande fra. Betingelserne i HAVING-klausulen kan henvise til aggregeringsfunktionerne, så det eksempel, der ikke virkede i WHERE-klausulen ovenfor, vil fungere fint i HAVING-klausulen: “HAVING sum(available_stock) > 0”.

Da vi allerede har grupperet dataene, kan vi ikke længere få adgang til de oprindelige rækker på dette tidspunkt, så vi kan kun anvende betingelser til at filtrere hele spande og ikke enkelte rækker i en spand.

Som vi nævnte i tidligere afsnit, kan vi heller ikke få adgang til aliaser, der er defineret i SELECT-klausulen, i afsnittet, da de endnu ikke er blevet evalueret af databasen (dette gælder i de fleste databaser).

Vinduesfunktioner

Hvis du bruger vinduesfunktioner, er dette det punkt, hvor de bliver udført. Ligesom grupperingsmekanismen udfører vinduesfunktioner også en beregning på et sæt rækker. Hovedforskellen er, at når du bruger Window-funktioner, beholder hver række sin egen identitet og bliver ikke grupperet i en spand med andre lignende rækker.

Window-funktioner kan kun bruges i enten SELECT- eller ORDER BY-klausulen. Du kan bruge aggregeringsfunktioner inden for vinduesfunktionerne, f.eks.:

SUM(COUNT(*)) OVER ()

SELECT-klausul

Nu, hvor vi er færdige med at kassere rækker fra datasættet og gruppere dataene, kan vi vælge de data, som vi ønsker at hente fra forespørgslen til klientsiden. Du kan bruge kolonnenavne, aggregeringer og underafspørgsler inde i SELECT-klausulen. Husk, at hvis du bruger en reference til en aggregeringsfunktion, f.eks. COUNT(*) i SELECT-klausulen, er det blot en reference til en aggregering, der allerede er sket, da grupperingen fandt sted, så selve aggregeringen sker ikke i SELECT-klausulen, men dette er blot en reference til dens resultatmængde.

DISTINCT nøgleordet

Syntaksen for DISTINCT nøgleordet er lidt forvirrende, fordi nøgleordet tager sin plads før kolonnenavnene i SELECT klausulen. Men den egentlige DISTINCT-operation finder sted efter SELECT-klausulen. Når du bruger DISTINCT-keywordet, vil databasen kassere rækker med duplikerede værdier fra de resterende rækker, der er tilbage, efter at filtreringen og aggregeringerne har fundet sted.

UNION-keyword

Med UNION-keywordet kombineres resultatsættene fra to forespørgsler til ét resultatsæt. De fleste databaser giver dig mulighed for at vælge mellem UNION DISTINCT (som udelukker duplikerede rækker fra det kombinerede resultatsæt) eller UNION ALL (som blot kombinerer resultatsættene uden at anvende nogen kontrol af duplikering).

Du kan anvende sortering (ORDER BY) og begrænsning (LIMIT) på UNION-resultatsættet, på samme måde som du kan anvende det på en almindelig forespørgsel.

ORDER BY-klausul

Sortering finder sted, når databasen har hele resultatsættet klar (efter filtrering, gruppering, fjernelse af duplikering). Når vi har det, kan databasen nu sortere resultatsættet ved hjælp af kolonner, udvalgte aliaser eller aggregeringsfunktioner, selv om de ikke er en del af de udvalgte data. Den eneste undtagelse er ved brug af nøgleordet DISTINCT, som forhindrer sortering efter en ikke-selekteret kolonne, da resultatmængdens rækkefølge i så fald vil være udefineret.

Du kan vælge at sortere dataene ved hjælp af en faldende (DESC) rækkefølge eller en opstigende (ASC) rækkefølge. Rækkefølgen kan være entydig for hver af rækkefølgedelene, så følgende er gyldigt: ORDER BY firstname ASC, age DESC

LIMIT og OFFSET

I de fleste anvendelsestilfælde (bortset fra nogle få som f.eks. rapportering) ønsker vi at kassere alle rækker undtagen de første X rækker i forespørgselsresultatet. LIMIT-klausulen, som udføres efter sortering, giver os mulighed for at gøre netop dette. Desuden kan du vælge, hvilken række du vil begynde at hente dataene fra, og hvor mange der skal udelukkes, ved hjælp af en kombination af nøgleordene LIMIT og OFFSET. Følgende eksempel vil hente 50 rækker fra række nr. 100: LIMIT 50 OFFSET 100

Optimer din database, auto-magisk.
Klik for at starte, helt gratis.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.