Den nye egenskab "Actual Rows Read" i eksekveringsplaner (som i SQL Server Management Studio vises som "Number of Rows Read") var en velkommen tilføjelse til ydeevnetunere. Det er som at have en ny supermagt, at være i stand til at fortælle betydningen af Seek Predicate v the Residual Predicate inden for en Seek-operatør. Jeg elsker dette, fordi det kan være virkelig vigtigt at forespørge.
Lad os se på to forespørgsler, som jeg kører mod AdventureWorks2012. De er meget enkle - den ene viser personer, der hedder John S, og den anden viser folk, der hedder J Smith. Som alle gode telefonbøger har vi et indeks på Efternavn, Fornavn.
select FirstName, LastName from Person.Person where LastName like 'S%' and FirstName = 'John'; select FirstName, LastName from Person.Person where LastName = 'Smith' and FirstName like 'J%';
Hvis du er nysgerrig, får jeg 2 rækker tilbage fra den første og 14 rækker tilbage fra den anden. Jeg er faktisk ikke så interesseret i resultaterne, jeg er interesseret i udførelsesplanerne.
Lad os se, hvad der sker. Jeg åbnede en ældre kopi af SQL Sentry Plan Explorer og åbnede mine planer side om side. I øvrigt – jeg havde kørt begge forespørgsler sammen, og så var begge planer i den samme .sqlplan-fil. Men jeg kunne åbne den samme fil to gange i PE og med glæde sidde dem side om side i fanegrupper.
Store. De ser ens ud! Jeg kan se, at søgningen til venstre producerer to rækker i stedet for fjorten - selvfølgelig er dette den bedste forespørgsel.
Men med et større vindue ville jeg have set mere information, og det er heldigt, at jeg havde kørt de to forespørgsler i samme batch.
Du kan se, at den anden forespørgsel, som producerede 14 rækker i stedet for 2 rækker, blev anslået til at tage over 80 % af omkostningerne! Hvis jeg ville køre forespørgslerne separat, ville hver af dem vise mig 100 %.
Lad os nu sammenligne med den seneste udgivelse af Plan Explorer.
Det, der springer ud for mig med det samme, er advarslen. Lad os se lidt nærmere.
Advarslen siger "Drift forårsagede resterende IO. Det faktiske antal læste rækker var 2.130, men antallet af returnerede rækker var 2." Sikkert nok, længere oppe ser vi "Faktiske rækker læst", der siger 2.130, og faktiske rækker ved 2.
Hov! For at finde disse rækker, skulle vi kigge igennem 2.130?
Du kan se, måden, som Seek kører på, er at starte med at tænke på Seek-prædikatet. Det er den, der udnytter indekset pænt, og som faktisk får operationen til at være en Seek. Uden et søgeprædikat bliver operationen en scanning. Nu, hvis dette søgeprædikat er garanteret at være højst én række (såsom når det har en lighedsoperator på et unikt indeks), så har vi en Singleton-søgning. Ellers har vi en Range Scan, og dette interval kan have et præfiks, en start og en slutning (men ikke nødvendigvis både en start og en slutning). Dette definerer rækkerne i tabellen, som vi er interesserede i for søgningen.
Men 'interesseret i' betyder ikke nødvendigvis 'returneret', for vi har måske mere arbejde at gøre. Det arbejde er beskrevet i det andet prædikat, som ofte er kendt som det resterende prædikat.
Nu hvor Residual Predicate faktisk gør det meste af arbejdet. Det er helt sikkert her – det filtrerer tingene ned fra 2.130 rækker til kun 2.
Range Scan starter i indekset ved "John S". Vi ved, at hvis der er en "John S", må dette være den første række, der kan tilfredsstille det hele. "Ian S" kan ikke. Så vi kan søge ind i indekset på det tidspunkt for at starte vores Range Scan. Hvis vi ser på Plan XML kan vi se dette eksplicit.
Bemærk, at vi ikke har et præfiks. Det gælder, når du har en lighed i første kolonne i indekset. Vi har bare StartRange og EndRange. Starten af området er "Greater Than or Equal" (GE) ScanType, ved værdien "S, John" (kolonnens referencer uden for skærmen er LastName, FirstName), og slutningen af området er "Less Than" ( LT) værdien T. Når scanningen rammer T, er den færdig. Ikke mere at gøre. The Seek har nu afsluttet sin Range Scan. Og i dette tilfælde returnerer den 2.130 rækker!
Bortset fra at den faktisk ikke returnerer 2.130 rækker, læser den bare 2.130 rækker. Navne som Barry Sai og Ken Sánchez læses, men kun de navne, der opfylder den næste kontrol, returneres - Residual-prædikatet, der sørger for, at fornavnet er John.
Indtastningen Faktisk Rows Read i Index Seek-operatørens egenskaber viser os denne værdi på 2.130. Og selvom det er synligt i tidligere udgivelser af Plan Explorer, får vi ikke en advarsel om det. Det er relativt nyt.
Vores anden forespørgsel (som leder efter J Smith) er meget pænere, og der er en grund til, at den blev anslået til at være mere end 4 gange billigere.
Her kender vi efternavnet nøjagtigt (Smith), og rækkeviddescanningen er på fornavnet (J%).
Det er her præfikset kommer ind.
Vi ser, at vores præfiks er en lighedsoperator (=, ScanType=”EQ”), og at efternavn skal være Smith. Vi har ikke engang overvejet begyndelsen eller slutningen af intervallet endnu, men præfikset fortæller os, at intervallet er inkluderet i den del af indekset, hvor Efternavn er Smith. Nu kan vi finde rækkerne>=J og
Der er stadig et restprædikat her, men dette er kun sikker på, at "LIKE J%" faktisk er testet. Selvom det virker intuitivt for os, at "LIKE J%" svarer nøjagtigt til ">=J og
Før Service Pack 3 af SQL Server 2012 havde vi ikke denne egenskab, og for at få en fornemmelse af forskellen mellem de faktiske rækker læst og de faktiske rækker, skulle vi bruge sporingsflag 9130. Her er de to planer med den TF slået til:
Du kan se, at der ikke er nogen advarsel denne gang, fordi Seek-operatøren returnerer alle 2130 rækker. Jeg tror, at hvis du bruger en version af SQL Server, der understøtter denne faktiske Rows Read, skal du stoppe med at bruge sporingsflaget 9130 i dine undersøgelser og begynde at se på advarslerne i Plan Explorer i stedet. Men mest af alt skal du forstå, hvordan dine operatører gør deres ting, for så vil du være i stand til at tolke, om du er tilfreds med planen, eller om du skal handle.
I et andet indlæg vil jeg vise dig en situation, hvor du måske foretrækker at se faktiske rækker læse være højere end faktiske rækker.
@rob_farley