ROW_NUMBER-vinduesfunktionen har adskillige praktiske anvendelser, langt ud over de åbenlyse placeringsbehov. Det meste af tiden, når du beregner rækkenumre, skal du beregne dem baseret på en rækkefølge, og du angiver den ønskede ordrespecifikation i funktionens vinduesrækkefølge. Der er dog tilfælde, hvor du har brug for at beregne rækkenumre i ingen bestemt rækkefølge; med andre ord baseret på ikke-bestemmende orden. Dette kan være på tværs af hele forespørgselsresultatet eller inden for partitioner. Eksempler inkluderer tildeling af unikke værdier til resultatrækker, deduplicering af data og returnering af en række pr. Gruppe.
Bemærk, at behovet for at tildele rækkenumre baseret på ikke-bestemmende rækkefølge er forskelligt fra behovet for at tildele dem baseret på tilfældig rækkefølge. Med førstnævnte er du bare ligeglad i hvilken rækkefølge de er tildelt, og om gentagne henrettelser af forespørgslen fortsat tildeler de samme rækkenumre til de samme rækker eller ej. Med sidstnævnte forventer du gentagne henrettelser for at fortsætte med at ændre, hvilke rækker der får tildelt hvilke rækkenumre. Denne artikel udforsker forskellige teknikker til beregning af rækkenumre med ikke-bestemmende rækkefølge. Håbet er at finde en teknik, der er både pålidelig og optimal.
Særlig tak til Paul White for tipet om konstant foldning, for runtime konstant teknik og for altid at være en god kilde til information!
Når orden betyder noget
Jeg starter med tilfælde, hvor rækkefølgen af række nummer betyder noget.
Jeg bruger en tabel kaldet T1 i mine eksempler. Brug følgende kode til at oprette denne tabel og udfylde den med eksempeldata:
Overvej følgende forespørgsel (vi kalder det Forespørgsel 1):
SELECT id, grp, datacol, ROW_NUMBER() OVER(PARTITION BY grp ORDER BY datacol) AS n FROM dbo.T1;
Her skal rækkenumre tildeles inden for hver gruppe identificeret af kolonne-grp, bestilt af kolonnens datacol. Da jeg kørte denne forespørgsel på mit system, fik jeg følgende output:
id grp datacol n--- ---- -------- ---5 A 40 12 A 50 211 A 50 37 B 10 13 B 20 2
Række numre tildeles her i en delvist deterministisk og delvist ikke-deterministisk rækkefølge. Hvad jeg mener med dette er, at du har en forsikring om, at inden for den samme partition vil en række med en større datacol-værdi få en større rækkeantalsværdi. Men da datacol ikke er unik inden for grp-partitionen, er rækkefølgen af tildeling af række numre mellem rækker med samme grp og datacol-værdier ikke-deterministisk. Sådan er tilfældet med rækkerne med id-værdierne 2 og 11. Begge har grp-værdien A og datacol-værdien 50. Da jeg udførte denne forespørgsel på mit system for første gang, fik rækken med id 2 række nummer 2 og række med id 11 fik række nummer 3. Husk sandsynligheden for, at dette sker i praksis i SQL Server; hvis jeg kører forespørgslen igen, teoretisk set, kunne rækken med id 2 tildeles række nummer 3 og rækken med id 11 kunne tildeles række nummer 2.
Hvis du har brug for at tildele rækkenumre baseret i en fuldstændig deterministisk rækkefølge, der garanterer gentagelige resultater på tværs af udførelser af forespørgslen, så længe de underliggende data ikke ændres, skal du bruge kombinationen af elementer i vinduespartitionering og ordningsklausuler for at være unik. Dette kan opnås i vores tilfælde ved at tilføje kolonne-id’et til vinduesordneklausulen som en tiebreaker. OVER-klausulen ville så være:
OVER (PARTITION BY grp ORDER BY datacol, id)
Under alle omstændigheder, når man beregner række numre baseret på en meningsfuld ordrespecifikation som i forespørgsel 1, skal SQL Server behandle rækker sorteret efter kombinationen af vinduespartitionering og bestillingselementer. Dette kan opnås ved enten at trække data forudbestilt fra et indeks eller ved at sortere dataene. I øjeblikket er der intet indeks på T1, der understøtter beregningen ROW_NUMBER i forespørgsel 1, så SQL Server skal vælge at sortere dataene. Dette kan ses i planen for forespørgsel 1 vist i figur 1.
Figur 1: Plan for forespørgsel 1 uden et understøttende indeks
Bemærk, at planen scanner dataene fra det klyngede indeks med en ordnet: falsk egenskab. Dette betyder, at scanningen ikke behøver at returnere de rækker, der er bestilt af indeksnøglen. Det er tilfældet, da det klyngede indeks bruges her bare fordi det tilfældigvis dækker forespørgslen og ikke på grund af dens nøglerækkefølge. Planen anvender derefter en sortering, hvilket resulterer i ekstra omkostninger, N Log N-skalering og forsinket svartid. Segmentoperatøren producerer et flag, der angiver, om rækken er den første i partitionen eller ej. Endelig tildeler sekvensprojektoperatøren rækkenumre startende med 1 i hver partition.
Hvis du vil undgå behovet for sortering, kan du forberede et dækningsindeks med en nøgleliste, der er baseret på partitionerings- og bestillingselementerne, og en inkluderingsliste, der er baseret på dækningselementer.Jeg kan godt lide at tænke på dette indeks som et POC-indeks (til partitionering, bestilling og dækning). Her er definitionen af POC, der understøtter vores forespørgsel:
CREATE INDEX idx_grp_data_i_id ON dbo.T1(grp, datacol) INCLUDE(id);
Kør forespørgsel 1 igen:
SELECT id, grp, datacol, ROW_NUMBER() OVER(PARTITION BY grp ORDER BY datacol) AS n FROM dbo.T1;
Planen for denne udførelse er vist i figur 2.
Figur 2: Plan for forespørgsel 1 med et POC-indeks
Vær opmærksom på, at denne gang scanner planen POC-indekset med en ordnet: sand ejendom. Det betyder, at scanningen garanterer, at rækkerne returneres i indeksnøglerækkefølge. Da dataene trækkes forudbestilt fra indekset, som vinduesfunktionen har brug for, er der ikke behov for eksplicit sortering. Skalering af denne plan er lineær, og svartiden er god.
Når orden ikke betyder noget
Tingene bliver lidt vanskelige, når du skal tildele række numre med en helt ikke-deterministisk rækkefølge. Den naturlige ting at ønske at gøre i et sådant tilfælde er at bruge ROW_NUMBER-funktionen uden at specificere en vinduesrækkefølge. Lad os først kontrollere, om SQL-standarden tillader dette. Her er den relevante del af standarden, der definerer syntaksreglerne for vinduet funktioner:
Bemærk, at punkt 6 viser funktionerne < ntile-funktion >, < lead- eller lag-funktion >, < rangfunktionstype > eller ROW_NUMBER, og derefter siger punkt 6a, at for funktionerne < ntile-funktion >, < bly- eller forsinkelsesfunktion >, RANK eller DENSE_RANK skal vinduesordnens klausul b den nuværende. Der er intet eksplicit sprog, der angiver, om ROW_NUMBER kræver en klausul om vinduesordre eller ej, men omtalelsen af funktionen i punkt 6 og dens udeladelse i 6a kan antyde, at klausulen er valgfri for denne funktion. Det er ret indlysende, hvorfor funktioner som RANK og DENSE_RANK kræver en vinduesordreklausul, da disse funktioner er specialiserede i håndtering af bånd, og bånd kun findes, når der er ordrespecifikation. Du kunne dog helt sikkert se, hvordan ROW_NUMBER-funktionen kunne drage fordel af en valgfri vinduesordneklausul.
Så lad os prøve, og forsøge at beregne rækkenumre uden vinduesbestilling i SQL Server:
SELECT id, grp, datacol, ROW_NUMBER() OVER() AS n FROM dbo.T1;
Dette forsøg resulterer i følgende fejl:
Funktionen “ROW_NUMBER” skal have en OVER-klausul med ORDER BY.
Hvis du kontrollerer SQL Servers dokumentation af ROW_NUMBER-funktionen, finder du følgende tekst:
ORDER BY-klausul bestemmer den rækkefølge, hvor rækkerne tildeles deres unikke ROW_NUMBER inden for en bestemt partition. Det er påkrævet. ”
Så åbenbart er vinduesrækkefølge obligatorisk for ROW_NUMBER-funktionen i SQL Server Det er forresten også tilfældet i Oracle.
Jeg må sige, at jeg ikke er sikker på, at jeg forstår årsagen ng bag dette krav. Husk, at du tillader at definere rækkenumre baseret på en delvist ikke-bestemmende rækkefølge, som i Forespørgsel 1. Så hvorfor ikke tillade ikke-bestemmelse hele vejen? Måske er der en eller anden grund, som jeg ikke tænker på. Hvis du kan tænke på en sådan grund, bedes du dele den.
Under alle omstændigheder kan du argumentere for, at hvis du ikke er ligeglad med ordren, da vinduesbestillingsbestemmelsen er obligatorisk, kan du angive enhver bestille. Problemet med denne tilgang er, at hvis du bestiller efter en eller anden kolonne fra de forespurgte tabeller, kan dette medføre en unødvendig præstationsstraff. Når der ikke er noget understøttende indeks på plads, betaler du for eksplicit sortering. Når der er et understøttende indeks på plads, begrænser du lagermotoren til en indeksordrescanningsstrategi (efter listen med indekslinket). Du tillader det ikke mere fleksibilitet, som det normalt har, når ordren ikke betyder noget ved at vælge mellem en indeksordrescanning og en allokeringsordrescanning (baseret på IAM-sider).
En idé, der er værd at prøve er at angive en konstant som 1 i vinduesrækkefølgen. Hvis det understøttes, ville du håbe, at optimeringsprogrammet er smart nok til at indse, at alle rækker har den samme værdi, så der er ingen reel ordrerelevans og derfor ikke behov for at tvinge en sortering eller en indeksordrescanning. Her er en forespørgsel, der forsøger denne tilgang:
SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY 1) AS n FROM dbo.T1;
Desværre understøtter SQL Server ikke denne løsning. Det genererer følgende fejl:
Funktioner med vindue, aggregater og NÆSTE VÆRDI FOR funktioner understøtter ikke heltal indekser som ORDER BY-sætningsudtryk.
Tilsyneladende antager SQL Server, at hvis du bruger en heltalskonstant i vinduesrækkefølgen, repræsenterer den en ordinær position for et element i SELECT-listen, som når du angiver et heltal i præsentationen ORDER BY-klausul. Hvis det er tilfældet, er en anden mulighed, der er værd at prøve, at angive en ikke-ikke-konstant konstant, som sådan:
SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY "No Order") AS n FROM dbo.T1;
Viser sig også, at denne løsning ikke understøttes. SQL Server genererer følgende fejl:
Funktioner med vindue, aggregater og NÆSTE VÆRDI FOR funktioner understøtter ikke konstanter som ORDER BY-udtryksudtryk.
Tilsyneladende understøtter vinduesordneklausulen ikke nogen form for konstant.
Indtil videre har vi lært følgende om ROW_NUMBER-funktionens vinduesbestillingsrelevans i SQL Server:
- ORDER BY kræves.
- Kan ikke bestille med et heltalskonstant, da SQL Server mener, at du prøver at specificere en ordinær position i SELECT.
- Kan ikke bestille efter enhver form for konstant.
Konklusionen er, at du skal bestille efter udtryk, der ikke er konstanter. Det er klart, at du kan bestille efter en kolonneliste fra de (n) forespurgte tabel (r). Men vi er på jagt efter at finde en effektiv løsning, hvor optimizer kan indse, at der ikke er nogen ordrerelevans.
Konstant foldning
Konklusionen hidtil er, at du ikke kan bruge konstanter i det ROW_NUMBERs vinduesrækkefølge, men hvad med udtryk baseret på konstanter, såsom i følgende forespørgsel:
SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY 1+0) AS n FROM dbo.T1;
Dette forsøg bliver imidlertid offer for en proces kendt som konstant foldning, hvilket normalt har en positiv præstationspåvirkning på forespørgsler. Ideen bag denne teknik er at forbedre forespørgselsydeevnen ved at folde noget udtryk baseret på konstanter til deres resultatkonstanter på et tidligt stadium af forespørgselsbehandlingen. Du kan finde detaljer om, hvilke slags udtryk der kan foldes konstant her. Vores udtryk 1 + 0 foldes til 1, hvilket resulterer i den samme fejl, som du fik, når du angiver konstant 1 direkte:
Windowed-funktioner, aggregater og NÆSTE VÆRDI FOR funktioner understøtter ikke heltalindekser som ORDER BY-sætningsudtryk.
Du vil stå over for en lignende situation, når du forsøger at sammenkæde to karakterstrengliteraler, sådan:
SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY "No" + " Order") AS n FROM dbo.T1;
Du får den samme fejl, som du fik, når du angiver den bogstavelige “Ingen ordre” direkte:
Windowed funktioner, aggregater og NÆSTE VÆRDI FOR funktioner understøtter ikke konstanter som ORDER BY-sætningsudtryk.
Bizarro verden – fejl, der forhindrer fejl
Livet er fuld af overraskelser…
En ting, der forhindrer konstant foldning, er, når udtrykket normalt ville resultere i en fejl. Eksempelvis kan udtrykket 2147483646 + 1 foldes konstant, da det resulterer i en gyldig INT-indtastet værdi. Følgelig mislykkes et forsøg på at køre følgende forespørgsel:
SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY 2147483646+1) AS n FROM dbo.T1;
Windowed-funktioner, aggregater og NÆSTE VÆRDI FOR-funktioner understøtter ikke heltalsindekser som ORDER BY-udtryksudtryk.
Imidlertid kan udtrykket 2147483647 + 1 ikke foldes konstant, fordi et sådant forsøg ville have resulteret i en INT-overflow-fejl. Implikationen ved bestilling er ret interessant. Prøv følgende forespørgsel (vi kalder denne forespørgsel 2):
SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY 2147483647+1) AS n FROM dbo.T1;
Mærkeligt nok kører denne forespørgsel med succes! Hvad der sker er, at SQL Server på den ene side ikke anvender konstant foldning, og derfor er ordren baseret på et udtryk, der ikke er en enkelt konstant. På den anden side beregner optimeren, at bestillingsværdien er den samme for alle rækker, så den ignorerer ordreudtrykket helt. Dette bekræftes, når planen for denne forespørgsel undersøges som vist i figur 3.
Figur 3: Plan for forespørgsel 2
Overhold at planen scanner noget dækningsindeks med en ordret: falsk ejendom. Dette var nøjagtigt vores præstationsmål.
På samme måde involverer følgende forespørgsel et vellykket konstant foldningsforsøg og mislykkes derfor:
SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY 1/1) AS n FROM dbo.T1;
Funktioner, der er slået sammen, aggregater og NÆSTE VERDI FOR funktioner understøtter ikke heltalsindekser som ORDER BY-udtryksudtryk.
Den følgende forespørgsel involverer et mislykket konstant foldningsforsøg og lykkes derfor ved at generere planen vist tidligere i figur 3:
SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY 1/0) AS n FROM dbo.T1;
Følgende forespørgsel involverer et vellykket konstant foldningsforsøg (VARCHAR bogstaveligt “1” konverteres implicit til INT 1, og derefter foldes 1 + 1 til 2) og mislykkes derfor:
SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY 1+"1") AS n FROM dbo.T1;
Funktioner med vindue, aggregater og NÆSTE VERDI FOR funktioner understøtter ikke heltal indekser som ORDER BY-sætningsudtryk.
Følgende forespørgsel involverer en mislykkedes konstant foldeforsøg (kan ikke konvertere “A” til INT), og det lykkes derfor at generere planen vist tidligere i figur 3:
SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY 1+"A") AS n FROM dbo.T1;
For at være ærlig, selvom denne bizarro-teknik opnår vores oprindelige præstationsmål, kan jeg ikke sige, at jeg betragter det som sikkert, og derfor er jeg ikke så behagelig at stole på det.
Kørselskonstanter baseret på funktioner
Fortsat søgning efter en god løsning til beregning af rækkenumre med ikke-bestemmende rækkefølge, er der et par teknikker, der virker mere sikre end den sidste skæve løsning: brug af runtime-konstanter baseret på funktioner, brug af en underforespørgsel baseret på en konstant, ved hjælp af en alias kolonne baseret på en konstant og ved hjælp af en variabel.
Som jeg forklarer i T-SQL-fejl, faldgruber og bedste praksis – determinisme, evalueres de fleste funktioner i T-SQL kun én gang pr. reference i forespørgslen – ikke en gang pr. række. Dette er tilfældet selv med de fleste ikke-deterministiske funktioner som GETDATE og RAND. Der er meget få undtagelser fra denne regel, som funktionerne NEWID og CRYPT_GEN_RANDOM, som bliver evalueret en gang pr. Række. De fleste funktioner, som GETDATE, @@ SPID og mange andre, evalueres en gang i starten af forespørgslen, og deres værdier betragtes derefter som runtime-konstanter. En henvisning til sådanne funktioner foldes ikke konstant. Disse egenskaber gør en runtime-konstant, der er baseret på en funktion, et godt valg som vinduesbestillingselement, og det ser faktisk ud til, at T-SQL understøtter det. Samtidig indser optimeringsprogrammet, at der i praksis ikke er nogen ordrerelevans, idet man undgår unødvendige præstationsstraffe.
Her er et eksempel på GETDATE-funktionen:
SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY GETDATE()) AS n FROM dbo.T1;
Denne forespørgsel får den samme plan som vist tidligere i figur 3.
Her er et andet eksempel ved hjælp af @@ SPID-funktionen (returnering af det aktuelle session-id):
SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY @@SPID) AS n FROM dbo.T1;
Hvad med funktionen PI? Prøv følgende forespørgsel:
SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY PI()) AS n FROM dbo.T1;
Denne fejler med følgende fejl:
Windowed-funktioner, aggregater og NÆSTE VÆRDI FOR funktioner understøtter ikke konstanter som ORDER BY-sætningsudtryk.
Funktioner som GETDATE og @@ SPID bliver revurderet en gang pr. udførelse af planen, så de ikke kan få konstant foldet. PI repræsenterer altid den samme konstant og bliver derfor konstant foldet.
Som tidligere nævnt er der meget få funktioner, der evalueres en gang pr. række, såsom NEWID og CRYPT_GEN_RANDOM. Dette gør dem til et dårligt valg som vinduesbestillingselement, hvis du har brug for ikke-bestemmende rækkefølge – ikke at forveksle med tilfældig rækkefølge. Hvorfor betale en unødvendig sorteringsstraf?
Her er et eksempel på NEWID-funktionen:
SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY NEWID()) AS n FROM dbo.T1;
Planen for denne forespørgsel er vist i figur 4, hvilket bekræfter, at SQL Server tilføjede eksplicit sortering baseret på funktionens resultat.
Figur 4: Planlæg for forespørgsel 3
Hvis du ønsker, at rækkenumrene skal tildeles i tilfældig rækkefølge, på alle måder, det er den teknik, du vil bruge. Du skal bare være opmærksom på, at det medfører sorteringsomkostningerne.
Brug af en underforespørgsel
Du kan også bruge en underforespørgsel baseret på en konstant som vinduesbestillingsudtryk (f.eks. BESTIL (VÆLG “Ingen ordre”). Også med denne løsning anerkender SQL Servers optimizer, at der ikke er nogen ordrerelevans, og pålægger derfor ikke en unødvendig sortering eller begrænser lagermotorens valg til dem, der skal garantere ordre. Prøv at køre følgende forespørgsel som et eksempel:
SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY (SELECT "No Order")) AS n FROM dbo.T1;
Du får den samme plan vist tidligere i figur 3.
En af de store fordele af denne teknik er, at du kan tilføje dit eget personlige præg.Måske kan du virkelig lide NULL’er:
SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS n FROM dbo.T1;
Måske kan du virkelig lide et bestemt antal:
SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY (SELECT 42)) AS n FROM dbo.T1;
Måske vil du sende nogen en besked:
SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY (SELECT "Lilach, will you marry me?")) AS n FROM dbo.T1;
Du forstår pointen.
Gennemførelig, men akavet
Der er et par teknikker, der fungerer, men er lidt akavet. Den ene er at definere et kolonnealias for et udtryk baseret på en konstant og derefter bruge dette kolonnealias som vinduesbestillingselement. Du kan gøre dette enten ved hjælp af et tabeludtryk eller med CROSS APPLY-operatoren og en tabelværdikonstruktør. Her er et eksempel på sidstnævnte:
SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY ) AS n FROM dbo.T1 CROSS APPLY ( VALUES("No Order") ) AS A();
Du får den samme plan vist tidligere i figur 3.
En anden mulighed er at bruge en variabel som vinduesbestillingselement:
DECLARE @ImABitUglyToo AS INT = NULL; SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY @ImABitUglyToo) AS n FROM dbo.T1;
Denne forespørgsel får også planen vist tidligere i figur 3.
Hvad hvis jeg bruger min egen UDF ?
Du tror måske, at brug af din egen UDF, der returnerer en konstant, kunne være et godt valg som vinduesbestillingselement, når du vil have en ikke-bestemmende rækkefølge, men det er det ikke. Overvej følgende UDF-definition som et eksempel:
DROP FUNCTION IF EXISTS dbo.YouWillRegretThis;GO CREATE FUNCTION dbo.YouWillRegretThis() RETURNS INTASBEGIN RETURN NULLEND;GO
Prøv at bruge UDF som vinduesbestillingsklausul som sådan (vi kalder denne forespørgsel 4):
SELECT id, grp, datacol, ROW_NUMBER() OVER(ORDER BY dbo.YouWillRegretThis()) AS n FROM dbo.T1;
Før SQL Server 2019 (eller parallelkompatibilitetsniveau < 150) bliver brugerdefinerede funktioner evalueret pr. række . Selv hvis de vender tilbage konstant, bliver de ikke indrammet. Derfor kan du på den ene side bruge en sådan UDF som vinduesbestillingselementet, men på den anden side resulterer dette i en slags straf. Dette bekræftes ved at undersøge planen for denne forespørgsel som vist i figur 5.
Figur 5: Plan for forespørgsel 4
Fra og med SQL Server 2019, under kompatibilitetsniveau > = 150, bliver sådanne brugerdefinerede funktioner inline, hvilket for det meste er en god ting, men i vores tilfælde resulterer i en fejl:
Funktioner med vindue, aggregater og NÆSTE VERDI FOR funktioner understøtter ikke konstanter som ORDER BY-udtryksudtryk.
Så ved hjælp af en UDF baseret på en konstant, da vinduesbestillingselementet enten tvinger en sortering eller en fejl afhængigt af den version af SQL Server, du bruger, og dit databasekompatibilitetsniveau. Kort sagt skal du ikke gøre dette.
Partitionerede rækkenumre med ikke-bestemmende rækkefølge
En almindelig brugstilfælde for partitionerede rækkenumre baseret på ikke-bestemmende rækkefølge returnerer enhver række pr. Gruppe. I betragtning af at der pr. Definition findes et partitioneringselement i dette scenarie, ville du tro, at en sikker teknik i et sådant tilfælde ville være at bruge vinduespartitioneringselementet også som vinduesbestillingselementet. Som et første trin beregner du rækkenumre som sådan:
SELECT id, grp, datacol, ROW_NUMBER() OVER(PARTITION BY grp ORDER BY grp) AS n FROM dbo.T1;
Planen for denne forespørgsel er vist i figur 6.
Figur 6: Plan for forespørgsel 5
Årsagen til, at vores understøttende indeks scannes med en ordnet: sand egenskab er, fordi SQL Server har brug for at behandle hver partitions rækker som en enkelt enhed. Det er tilfældet inden filtrering. Hvis du kun filtrerer en række pr. Partition, har du både ordrebaserede og hash-baserede algoritmer som valgmuligheder.
Det andet trin er at placere forespørgslen med beregning af række nummer i et tabeludtryk og i den ydre forespørgsel filtrerer rækken med række nummer 1 i hver partition som sådan:
Teoretisk set skal denne teknik være sikker, men Paul white fandt en fejl, der viser, at man ved hjælp af denne metode kan få attributter fra forskellige kilderækker i den returnerede resultatrække pr. partition. Brug af en runtime-konstant baseret på en funktion eller en underforespørgsel baseret på en konstant, da bestillingselementet ser ud til at være sikkert selv i dette scenario, så sørg for at bruge en løsning som følgende i stedet:
Ingen skal passere denne vej uden min tilladelse
At prøve at beregne rækkenumre baseret på ikke-bestemmende rækkefølge er et almindeligt behov. Det ville have været rart, hvis T-SQL simpelthen gjorde vinduesordnsbestemmelsen valgfri for ROW_NUMBER-funktionen, men det gør det ikke. Hvis ikke, ville det have været rart, hvis det i det mindste tillod at bruge en konstant som bestillingselement, men det er heller ikke en understøttet mulighed.Men hvis du spørger pænt, i form af en underforespørgsel baseret på en konstant eller en runtime-konstant baseret på en funktion, tillader SQL Server det. Dette er de to muligheder, som jeg er mest fortrolig med. Jeg føler mig ikke rigtig godt tilpas med de skæve, fejlagtige udtryk, der ser ud til at virke, så jeg kan ikke anbefale denne mulighed.