SQL-vinduesfunktioner

Starter du her? Denne lektion er en del af en tutorial i fuld længde i brug af SQL til dataanalyse. Se begyndelsen.

I denne lektion dækker vi:

  • Introduktion til vinduesfunktioner
  • Grundlæggende syntaks for vinduesvindue
  • De sædvanlige mistænkte: SUM, COUNT og AVG
  • ROW_NUMBER ()
  • RANK () og DENSE_RANK ()
  • NTILE
  • LAG og LEAD
  • Definition af et vinduesalias
  • Avancerede vindueteknikker

Denne lektion bruger data fra Washington DCs Capital Bikeshare Program, der udgiver detaljerede historiske data på tripniveau på deres hjemmeside. Dataene blev downloadet i februar 2014, men er begrænset til data indsamlet i første kvartal af 2012. Hver række repræsenterer en tur. De fleste felter er selvforklarende, undtagen rider_type: “Registreret” angiver et månedligt medlemskab af rideshare-programmet, “Casual” tilskynder til, at rytteren købte et 3-dages pas. start_time og end_time felterne blev renset fra deres originale formularer, så de passer til SQL-datoformatering – de er gemt i denne tabel som tidsstempler.

Introduktion til vinduesfunktioner

Dokumentationen fra PostgreSQL gør et fremragende stykke arbejde med at introducere konceptet med Window-funktioner:

En vinduesfunktion udfører en beregning på tværs af et sæt tabelrækker, der på en eller anden måde er relateret til den aktuelle række. Dette kan sammenlignes med den type beregning, der kan udføres med en samlet funktion. Men i modsætning til almindelige samlede funktioner, brug af et vindue funktion får ikke rækker til at blive grupperet i en enkelt outputrække – rækkerne bevarer deres separate identitet. Bag kulisserne er vinduesfunktionen i stand til at få adgang til mere end bare den aktuelle række i forespørgselsresultatet.

Det mest praktiske eksempel på dette er et løbende total:

Du kan se, at ovenstående forespørgsel skaber en sammenlægning (running_total) uden at bruge GROUP BY. Lad os nedbryde syntaksen og se, hvordan den fungerer.

Grundlæggende syntaks for vinduer

Den første del af ovenstående sammenlægning, SUM(duration_seconds) , ligner meget enhver anden sammenlægning. Tilføjelse af OVER betegner det som en vinduesfunktion. Du kan læse ovenstående sammenlægning som “tag summen af duration_seconds over hele resultatsættet, i rækkefølge efter start_time. “

Hvis du gerne vil indsnævre vinduet fra hele datasættet til individuelle grupper inden for datasættet kan du bruge PARTITION BY til at gøre det:

Ovenstående forespørgsel grupperer og ordrer forespørgslen ved start_terminal . Inden for hver værdi af start_terminal ordnes den efter start_time, og de løbende samlede summer på tværs af den aktuelle række og alle tidligere rækker af duration_seconds. Rul ned, indtil værdien start_terminal ændres, og du vil bemærke, at running_total starter forfra. Det er, hvad der sker, når du grupperer ved hjælp af PARTITION BY. I tilfælde af at du stadig er stumpet af ORDER BY, bestiller den simpelthen efter den udpegede kolonne (s) på samme måde som ORDER BY -sætningen ville, bortset fra at den behandler hver partition som separat. Det opretter også det samlede løb – uden ORDER BY, vil hver værdi simpelthen være en sum af alle duration_seconds -værdierne i den respektive start_terminal. Prøv at køre ovenstående forespørgsel uden ORDER BY for at få en idé:

ORDER og PARTITION definer det, der kaldes” vinduet “- den ordnede delmængde af data, som beregningerne foretages over.

Bemærk: Du kan ikke bruge vinduesfunktioner og standardaggregeringer i det samme Mere specifikt kan du ikke inkludere vinduesfunktioner i en GROUP BY -klausul.

Øvelsesproblem

Skriv en forespørgsel modifikation af ovenstående eksempel forespørgsel, der viser varigheden af hver tur som en procentdel af den samlede tid, der er påløbet af ryttere fra hver start_terminal

Prøv det Se svaret

De sædvanlige mistænkte: SUM, COUNT og AVG

Når du bruger vinduesfunktioner, kan du anvende de samme aggregater, som du ville gøre under normale omstændigheder— SUM, COUNT og AVG. Den nemmeste måde at forstå disse på er at genkøre det foregående eksempel med nogle ekstra funktioner. Lav

Alternativt fungerer de samme funktioner med ORDER BY:

Sørg for at sætte de to foregående forespørgsler i Mode og køre dem. Dette næste praksis-problem svarer meget til eksemplerne, så prøv at ændre ovenstående kode i stedet for at starte fra bunden.

Øvelsesopgave

Skriv en forespørgsel, der viser en løbende total af varigheden af cykelture (svarende til sidste eksempel), men grupperet efter end_terminal og med kørevarighed sorteret i faldende rækkefølge.

Prøv det Se svaret

ROW_NUMBER ()

ROW_NUMBER() gør, hvad det lyder – viser nummeret på en given række. Det starter er 1 og nummererer rækkerne i henhold til ORDER BY -delen af vindussætningen. ROW_NUMBER() kræver ikke, at du angiver en variabel inden for parenteserne:

Brug af PARTITION BY -klausulen giver dig mulighed for begynde at tælle 1 igen i hver partition. Følgende forespørgsel starter optællingen igen for hver terminal:

RANK () og DENSE_RANK ()

RANK() er lidt anderledes end ROW_NUMBER(). Hvis du f.eks. Bestiller med start_time, kan det være tilfældet, at nogle terminaler har forlystelser med to identiske starttider. I dette tilfælde får de samme rang, mens ROW_NUMBER() giver dem forskellige tal. I den følgende forespørgsel bemærker du 4. og 5. observation for start_terminal 31000 — de får begge en rang på 4, og følgende resultat får en rang på 6:

Du kan også bruge DENSE_RANK() i stedet for RANK() afhængigt af din applikation. Forestil dig en situation, hvor tre poster har samme værdi. Ved hjælp af begge kommandoer får de alle samme rang. Af hensyn til dette eksempel, lad “s sige det” s “2.” Her er hvordan de to kommandoer evaluerer de næste resultater forskelligt:

  • RANK() giver de samme rækker en rang på 2, så spring ranger 3 over og 4, så det næste resultat ville være 5
  • DENSE_RANK() ville stadig give alle de samme rækker en rang på 2, men den følgende række ville være 3 — nej rækker springes over.

Øvelsesproblem

Skriv en forespørgsel, der viser de 5 længste forlystelser fra hver startterminal, ordnet efter terminal, og længste til korteste forlystelser inden for hver terminal. Begræns til forlystelser, der fandt sted inden 8. januar 2012.

Prøv det Se svaret

NTILE

Du kan bruge vinduesfunktioner til at identificere, hvilken percentil (eller kvartil eller enhver anden underinddeling) en given række falder i. Syntaksen er NTILE(*# of buckets*) I dette tilfælde bestemmer ORDER BY hvilken kolonne, der skal bruges til at bestemme kvartilerne (eller hvilket antal af “fliser du angive). For eksempel:

Når du ser på resultaterne fra forespørgslen ovenfor, kan du se, at kolonnen percentile ikke beregner nøjagtigt som du kunne forvente. Hvis du kun havde to poster, og du målte percentiler, du ville forvente, at en post definerede 1. percentil, og den anden record definerede 100. percentil. Ved hjælp af NTILE -funktionen er det, du faktisk ser, en post i 1. percentilen og en i 2. percentilen. Du kan se dette i resultaterne for start_terminal 31000 — kolonnen percentile ligner bare en numerisk placering. Hvis du ruller ned til start_terminal 31007, kan se, at det korrekt beregner percentiler, fordi der er mere end 100 poster for det start_terminal. Hvis du arbejder med meget små vinduer, skal du huske dette og overveje at bruge kvartiler eller lignende små bånd.

Øvelsesproblem

Skriv en forespørgsel, der kun viser varigheden af turen og percentilen, hvortil denne varighed falder (på tværs af hele datasættet – ikke opdelt efter terminal).

Prøv det Se svaret

LAG og LEAD

Det kan ofte være nyttigt at sammenligne rækker med foregående eller efterfølgende rækker, især hvis du har fået dataene i en rækkefølge, der giver mening. Du kan bruge LAG eller LEAD til at oprette kolonner, der trækker værdier fra andre rækker – alt hvad du skal gøre er at indtaste, hvilken kolonne du vil trække fra, og hvor mange rækker væk du gerne vil trække. LAG trækker fra tidligere rækker og LEAD trækker fra følgende rækker:

Dette er især nyttigt, hvis du vil beregne forskelle mellem rækker:

Den første række i difference kolonnen er nul, fordi der ikke er nogen tidligere række, hvorfra man kan trække. Tilsvarende vil brug af LEAD skabe nuller i slutningen af datasættet. Hvis du gerne vil gøre resultaterne lidt renere, kan du pakke det ind i en ydre forespørgsel for at fjerne nul:

Definere et vinduesalias

Hvis du planlægger at skrive flere vinduer fungerer i den samme forespørgsel ved hjælp af det samme vindue kan du oprette et alias.Tag NTILE eksemplet ovenfor:

Dette kan omskrives som:

WINDOW klausul, hvis den er inkluderet, skal altid komme efter WHERE klausulen.

Avancerede vindueteknikker

Du kan tjekke en komplet liste over vinduer funktioner i Postgres (syntaks-tilstand bruger) i Postgres-dokumentationen. Hvis du bruger vinduesfunktioner i en tilsluttet database, skal du se på den relevante syntaksvejledning til dit system.

Leave a Reply

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *