sql >> Database teknologi >  >> RDS >> Database

Fundamentals of Table Expressions, Del 13 – Inline Table-Valued Functions, Fortsat

Dette er det trettende og sidste afsnit i en serie om tabeludtryk. I denne måned fortsætter jeg diskussionen, jeg startede i sidste måned, om inline-tabel-værdisatte funktioner (iTVF'er).

I sidste måned forklarede jeg, at når SQL Server inlines iTVF'er, der forespørges med konstanter som input, anvender den parameterindlejringsoptimering som standard. Parameterindlejring betyder, at SQL Server erstatter parameterreferencer i forespørgslen med de bogstavelige konstantværdier fra den aktuelle udførelse, og så bliver koden med konstanterne optimeret. Denne proces muliggør forenklinger, der kan resultere i mere optimale forespørgselsplaner. I denne måned uddyber jeg emnet og dækker specifikke tilfælde for sådanne forenklinger som konstant foldning og dynamisk filtrering og bestilling. Hvis du har brug for en genopfriskning af optimering af parameterindlejring, kan du gennemgå sidste måneds artikel samt Paul Whites fremragende artikel Parameter Sniffing, Embedding og RECOMPILE Options.

I mine eksempler vil jeg bruge en prøvedatabase kaldet TSQLV5. Du kan finde scriptet, der opretter og udfylder det her, og dets ER-diagram her.

Konstant foldning

I de tidlige stadier af forespørgselsbehandlingen evaluerer SQL Server visse udtryk, der involverer konstanter, og folder dem til resultatkonstanterne. For eksempel kan udtrykket 40 + 2 foldes til konstanten 42. Du kan finde reglerne for foldbare og ikke-foldbare udtryk her under "Konstant foldning og udtryksevaluering."

Det interessante med hensyn til iTVF'er er, at takket være parameterindlejringsoptimering kan forespørgsler, der involverer iTVF'er, hvor du sender konstanter som input, under de rette omstændigheder drage fordel af konstant foldning. At kende reglerne for foldbare og ikke-foldbare udtryk kan påvirke den måde, du implementerer dine iTVF'er på. I nogle tilfælde kan du ved at anvende meget subtile ændringer på dine udtryk aktivere mere optimale planer med bedre udnyttelse af indeksering.

Som et eksempel kan du overveje følgende implementering af en iTVF kaldet Sales.MyOrders:

BRUG TSQLV5;GO CREATE OR ALTER FUNCTION Sales.MyOrders ( @add AS INT, @subtract AS INT )RETURNER TABLEASRETURN SELECT orderid + @add - @subtract AS myorderid, orderdate, custid, empid FROM Sales.Orders;GO

Udsted følgende forespørgsel, der involverer iTVF (jeg vil referere til dette som forespørgsel 1):

SELECT myorderid, orderdate, custid, empidFROM Sales.MyOrders(1, 10248)ORDER BY myorderid;

Planen for forespørgsel 1 er vist i figur 1.

Figur 1:Plan for forespørgsel 1

Det klyngede indeks PK_Orders er defineret med orderid som nøglen. Havde konstant foldning fundet sted her efter parameterindlejring, ville ordreudtrykket orderid + 1 – 10248 være blevet foldet til orderid – 10247. Dette udtryk ville være blevet betragtet som et ordensbevarende udtryk med hensyn til orderid og ville som sådan have aktiveret optimizer til at stole på indeksrækkefølge. Ak, det er ikke tilfældet, som det fremgår af den eksplicitte Sorter-operatør i planen. Hvad skete der?

Konstante folderegler er kræsne. Udtrykket kolonne1 + konstant1 – konstant2 evalueres fra venstre mod højre til konstant foldning. Den første del, kolonne1 + konstant1 er ikke foldet. Lad os kalde dette udtryk 1. Den næste del, der evalueres, behandles som udtryk1 - konstant2, som heller ikke er foldet. Uden foldning anses et udtryk i formen kolonne1 + konstant1 – konstant2 ikke for ordensbevarende i forhold til kolonne1, og kan derfor ikke stole på indeksbestilling, selvom du har et understøttende indeks på kolonne1. På samme måde kan udtrykket konstant1 + kolonne1 – konstant2 ikke foldes konstant. Udtrykket konstant1 – konstant2 + kolonne1 kan dog foldes. Mere specifikt er den første del konstant1 – konstant2 foldet til en enkelt konstant (lad os kalde det konstant3), hvilket resulterer i udtrykket konstant3 + kolonne1. Dette udtryk betragtes som et ordensbevarende udtryk med hensyn til kolonne1. Så så længe du sørger for at skrive dit udtryk ved hjælp af den sidste formular, kan du aktivere optimeringsværktøjet til at stole på indeksrækkefølge.

Overvej følgende forespørgsler (jeg vil referere til dem som forespørgsel 2, forespørgsel 3 og forespørgsel 4), og før du ser på forespørgselsplanerne, skal du se, om du kan se, hvilke der involverer eksplicit sortering i planen, og hvilke der ikke vil:

-- Forespørgsel 2SELECT orderid + 1 - 10248 AS myorderid, orderdate, custid, empidFROM Sales.OrdersORDER BY myorderid; -- Forespørgsel 3SELECT 1 + orderid - 10248 AS myorderid, orderdate, custid, empidFROM Sales.OrdersORDER BY myorderid; -- Forespørgsel 4SELECT 1 - 10248 + orderid AS myorderid, orderdate, custid, empidFROM Sales.OrdersORDER BY myorderid;

Undersøg nu planerne for disse forespørgsler som vist i figur 2.

Figur 2:Planer for forespørgsel 2, forespørgsel 3 og forespørgsel 4

Undersøg Compute Scalar-operatørerne i de tre planer. Kun planen for forespørgsel 4 pådrog sig konstant foldning, hvilket resulterede i et ordensudtryk, der anses for at være ordensbevarende med hensyn til orderid, hvilket undgår eksplicit sortering.

For at forstå dette aspekt af konstant foldning, kan du nemt rette iTVF ved at ændre udtrykket orderid + @add – @subtract til @add – @subtract + orderid, som sådan:

OPRET ELLER ÆNDRING AF FUNKTION Sales.MyOrders ( @add AS INT, @subtract AS INT )RETURNER TABLEASRETURN SELECT @add - @subtract + orderid AS myorderid, orderdate, custid, empid FROM Sales.Orders;GO

Forespørg funktionen igen (jeg vil referere til dette som forespørgsel 5):

SELECT myorderid, orderdate, custid, empidFROM Sales.MyOrders(1, 10248)ORDER BY myorderid;

Planen for denne forespørgsel er vist i figur 3.

Figur 3:Plan for forespørgsel 5

Som du kan se, oplevede forespørgslen denne gang konstant foldning, og optimeringsværktøjet var i stand til at stole på indeksbestilling og undgå eksplicit sortering.

Jeg brugte et simpelt eksempel til at demonstrere denne optimeringsteknik, og som sådan kan det virke lidt fortænkt. Du kan finde en praktisk anvendelse af denne teknik i artiklen Number series generator challenge solutions – Part 1.

Dynamisk filtrering/bestilling

I sidste måned dækkede jeg forskellen mellem den måde, SQL Server optimerer en forespørgsel på i en iTVF versus den samme forespørgsel i en lagret procedure. SQL Server vil typisk anvende parameterindlejringsoptimering som standard for en forespørgsel, der involverer en iTVF med konstanter som input, men optimerer den parametriserede form af en forespørgsel i en lagret procedure. Men hvis du tilføjer OPTION(RECOMPILE) til forespørgslen i den lagrede procedure, vil SQL Server typisk også anvende parameterindlejringsoptimering i dette tilfælde. Fordelene i iTVF-sagen inkluderer det faktum, at du kan involvere det i en forespørgsel, og så længe du passerer gentagne konstante input, er der potentiale til at genbruge en tidligere cachelagret plan. Med en lagret procedure kan du ikke involvere den i en forespørgsel, og hvis du tilføjer OPTION(RECOMPILE) for at få parameterindlejringsoptimering, er der ingen mulighed for plangenbrug. Den lagrede procedure giver meget mere fleksibilitet med hensyn til de kodeelementer, du kan bruge.

Lad os se, hvordan alt dette udspiller sig i en klassisk parameterindlejrings- og bestillingsopgave. Følgende er en forenklet lagret procedure, der anvender dynamisk filtrering og sortering svarende til den, Paul brugte i sin artikel:

OPRET ELLER ÆNDRING PROCEDURE HR.GetEmpsP @lastnamepattern AS NVARCHAR(50), @sort AS TINYINTASSET NOCOUNT ON; VÆLG empid, fornavn, efternavn FRA HR.Medarbejdere HVOR efternavn SOM @efternavnmønster ELLER @efternavnmønster ER NULLORDER BY CASE WHEN @sort =1 SÅ empid END, CASE WHEN @sort =2 THEN firstname END, CASE WHEN @NGO-efternavn =END; 

Bemærk, at den aktuelle implementering af den lagrede procedure ikke inkluderer OPTION(RECOMPILE) i forespørgslen.

Overvej følgende udførelse af den lagrede procedure:

EXEC HR.GetEmpsP @lastnamepattern =N'D%', @sort =3;

Planen for denne udførelse er vist i figur 4.

Figur 4:Plan for procedure HR.GetEmpsP

Der er et indeks defineret i kolonnen efternavn. Teoretisk set kan indekset med de nuværende input være gavnligt både for filtreringsbehovet (med en søgning) og bestillingsbehovet (med en ordnet:sand range scanning) for forespørgslen. Men da SQL Server som standard optimerer den parametriserede form af forespørgslen og ikke anvender parameterindlejring, anvender den ikke de forenklinger, der kræves for at kunne drage fordel af indekset til både filtrerings- og bestillingsformål. Så planen kan genbruges, men den er ikke optimal.

For at se, hvordan tingene ændrer sig med parameterindlejringsoptimering, skal du ændre den lagrede procedureforespørgsel ved at tilføje OPTION(RECOMPILE), som sådan:

OPRET ELLER ÆNDRING PROCEDURE HR.GetEmpsP @lastnamepattern AS NVARCHAR(50), @sort AS TINYINTASSET NOCOUNT ON; VÆLG empid, fornavn, efternavn FRA HR.Medarbejdere HVOR efternavn SOM @efternavnmønster ELLER @efternavnmønster ER NULLORDER BY CASE WHEN @sort =1 SÅ empid END, CASE WHEN @sort =2 THEN fornavn END, CASE, NÅR 3OPTION THENRECOMLE( );GO

Udfør den lagrede procedure igen med de samme input, som du brugte før:

EXEC HR.GetEmpsP @lastnamepattern =N'D%', @sort =3;

Planen for denne udførelse er vist i figur 5.

Figur 5:Plan for procedure HR.GetEmpsP With OPTION(RECOMPILE)

Som du kan se, takket være parameterindlejringsoptimering, var SQL Server i stand til at forenkle filterprædikatet til det sargerbare prædikat efternavn LIKE N'D%', og rækkefølgelisten til NULL, NULL, efternavn. Begge elementer kunne drage fordel af indekset på efternavn, og derfor viser planen en søgning i indekset og ingen eksplicit sortering.

Teoretisk forventer du at kunne få lignende forenkling, hvis du implementerer forespørgslen i en iTVF, og dermed lignende optimeringsfordele, men med mulighed for at genbruge cachelagrede planer, når de samme inputværdier genbruges. Så lad os prøve...

Her er et forsøg på at implementere den samme forespørgsel i en iTVF (kør ikke denne kode endnu):

CREATE OR ALTER FUNCTION HR.GetEmpsF( @lastnamepattern AS NVARCHAR(50), @sort AS TINYINT)RETURNER TABLEASRETURN SELECT empid, forname, lastname FROM HR.Employees WHERE lastname LIKE @lastnamepattern OR ISBY NU CASE Pattern ELLER ISBY NULLASE @sort =1 THEN empid END, CASE WHEN @sort =2 THEN fornavn END, CASE WHEN @sort =3 THEN efternavn END;GO

Før du forsøger at udføre denne kode, kan du så se et problem med denne implementering? Husk, at jeg tidligt i denne serie forklarede, at et tabeludtryk er en tabel. En tabels krop er et sæt (eller multisæt) af rækker og har som sådan ingen rækkefølge. Derfor kan en forespørgsel, der bruges som et tabeludtryk, normalt ikke have et ORDER BY-udtryk. Faktisk, hvis du prøver at køre denne kode, får du følgende fejlmeddelelse:

Msg 1033, Level 15, State 1, Procedure GetEmps, Line 16 [Batch Start Line 128]
ORDER BY-sætningen er ugyldig i visninger, inline-funktioner, afledte tabeller, underforespørgsler og almindelige tabeludtryk, medmindre TOP, OFFSET eller FOR XML er også angivet.

Sikker, som fejlen siger, vil SQL Server gøre en undtagelse, hvis du bruger et filtreringselement som TOP eller OFFSET-FETCH, som er afhængig af ORDER BY-klausulen til at definere bestillingsaspektet af filteret. Men selvom du medtager en ORDER BY-klausul i den indre forespørgsel takket være denne undtagelse, får du stadig ingen garanti for rækkefølgen af ​​resultatet i en ydre forespørgsel mod tabeludtrykket, medmindre det har sin egen ORDER BY-klausul .

Hvis du stadig ønsker at implementere forespørgslen i en iTVF, kan du få den indre forespørgsel til at håndtere den dynamiske filtreringsdel, men ikke den dynamiske rækkefølge, som sådan:

OPRET ELLER ÆNDRING AF FUNKTION HR.GetEmpsF( @lastnamepattern AS NVARCHAR(50))RETURNER TABLEASRETURN VÆLG empid, fornavn, efternavn FRA HR.Medarbejdere HVOR efternavn SOM @lastnamepattern ELLER @lastnamepattern ER NULL; 

Selvfølgelig kan du få den ydre forespørgsel til at håndtere ethvert specifikt bestillingsbehov, som i følgende kode (jeg vil referere til dette som forespørgsel 6):

VÆLG empid, fornavn, efternavnFRA HR.GetEmpsF(N'D%')ORDER BY efternavn;

Planen for denne forespørgsel er vist i figur 6.

Figur 6:Plan for forespørgsel 6

Takket være inlining og parameterindlejring ligner planen den, der er vist tidligere for den lagrede procedure-forespørgsel i figur 5. Planen er effektivt afhængig af indekset til både filtrerings- og bestillingsformål. Du får dog ikke den fleksibilitet af den dynamiske ordreindgang, som du havde med den lagrede procedure. Du skal være eksplicit med rækkefølgen i ORDER BY-klausulen i forespørgslen mod funktionen.

Følgende eksempel har en forespørgsel mod funktionen uden filtrering og ingen bestillingskrav (jeg vil referere til dette som forespørgsel 7):

VÆLG empid, fornavn, efternavnFRA HR.GetEmpsF(NULL);

Planen for denne forespørgsel er vist i figur 7.

Figur 7:Plan for forespørgsel 7

Efter inlining og parameterindlejring forenkles forespørgslen, så den ikke har noget filterprædikat og ingen rækkefølge, og den bliver optimeret med en fuld uordnet scanning af det klyngede indeks.

Til sidst skal du forespørge efter funktionen med N'D%' som det indtastede efternavnsfiltreringsmønster og sortere resultatet efter fornavnskolonnen (jeg vil referere til dette som forespørgsel 8):

VÆLG empid, fornavn, efternavnFRA HR.GetEmpsF(N'D%')ORDER BY forname;

Planen for denne forespørgsel er vist i figur 8.

Figur 8:Plan for forespørgsel 8

Efter forenkling involverer forespørgslen kun filtreringsprædikatet efternavn LIKE N'D%' og bestillingselementet fornavn. Denne gang valgte optimeringsværktøjet at anvende en uordnet scanning af det klyngede indeks med det resterende prædikat efternavn LIKE N'D%', efterfulgt af eksplicit sortering. Den valgte ikke at anvende en søgning i indekset på efternavn, fordi indekset ikke er dækkende, tabellen er så lille, og indeksbestillingen ikke er gavnlig for de aktuelle forespørgselsbestillingsbehov. Der er heller ikke defineret noget indeks på fornavnskolonnen, så en eksplicit sortering skal alligevel anvendes.

Konklusion

Standardparameterindlejringsoptimeringen af ​​iTVF'er kan også resultere i konstant foldning, hvilket muliggør mere optimale planer. Du skal dog være opmærksom på konstante folderegler for at bestemme, hvordan du bedst formulerer dine udtryk.

Implementering af logik i en iTVF har fordele og ulemper sammenlignet med implementering af logik i en lagret procedure. Hvis du ikke er interesseret i optimering af parameterindlejring, kan standardparametriserede forespørgselsoptimeringer af lagrede procedurer resultere i mere optimal plancaching og genbrugsadfærd. I de tilfælde, hvor du er interesseret i parameterindlejringsoptimering, får du det typisk som standard med iTVF'er. For at få denne optimering med lagrede procedurer skal du tilføje forespørgselsindstillingen RECOMPILE, men så får du ikke plangenbrug. I det mindste med iTVF'er kan du få plangenbrug, forudsat at de samme parameterværdier gentages. Så igen har du mindre fleksibilitet med de forespørgselselementer, du kan bruge i en iTVF; for eksempel må du ikke have en præsentation ORDER BY-klausul.

Tilbage til hele serien om tabeludtryk, synes jeg, at emnet er super vigtigt for databaseudøvere. Den mere komplette serie omfatter underserien på nummerseriegeneratoren, som er implementeret som en iTVF. I alt omfatter serien følgende 19 dele:

  • Grundlæggende om tabeludtryk, del 1
  • Grundlæggende om tabeludtryk, del 2 – Afledte tabeller, logiske overvejelser
  • Grundlæggende om tabeludtryk, del 3 – Afledte tabeller, optimeringsovervejelser
  • Grundlæggende om tabeludtryk, del 4 – Afledte tabeller, optimeringsovervejelser, fortsat
  • Grundlæggende om tabeludtryk, del 5 – CTE'er, logiske overvejelser
  • Grundlæggende om tabeludtryk, del 6 – Rekursive CTE'er
  • Grundlæggende om tabeludtryk, del 7 – CTE'er, optimeringsovervejelser
  • Grundlæggende om tabeludtryk, del 8 – CTE'er, optimeringsovervejelser fortsatte
  • Grundlæggende om tabeludtryk, del 9 – Visninger, sammenlignet med afledte tabeller og CTE'er
  • Grundlæggende om tabeludtryk, del 10 – Visninger, SELECT * og DDL-ændringer
  • Grundlæggende om tabeludtryk, del 11 – Visninger, ændringsovervejelser
  • Grundlæggende om tabeludtryk, del 12 – Inline-tabelværdierede funktioner
  • Fundamentals of Table Expressions, Del 13 – Inline Table-Valued Functions, fortsat
  • Udfordringen er startet! Fællesskabsopfordring til at skabe den hurtigste nummerseriegenerator
  • Udfordringsløsninger til talseriegeneratorer – del 1
  • Udfordringsløsninger til talseriegeneratorer – del 2
  • Udfordringsløsninger til talseriegeneratorer – del 3
  • Udfordringsløsninger til talseriegeneratorer – del 4
  • Udfordringsløsninger til Nummerseriegeneratorer – Del 5

  1. MySQL Vis indekser i databasen

  2. gemme arabisk i SQL-database

  3. Importer CSV-fil direkte til MySQL

  4. MySQL pivottabelforespørgsel med dynamiske kolonner