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

Hjælper tildeling af inputparametre for lagrede procedurer til lokale variabler med at optimere forespørgslen?

Jeg vil ikke forsøge at forklare de fulde detaljer om parametersniffing, men kort sagt nej det gør det ikke altid hjælpe (og det kan hindre).

Forestil dig en tabel (T) med en primær nøgle og en indekseret datokolonne (A), i tabellen er der 1.000 rækker, 400 har samme værdi af A (lad os sige i dag 20130122), de resterende 600 rækker er de næste 600 dage , så kun 1 post pr. dato.

Denne forespørgsel:

SELECT *
FROM T
WHERE A = '20130122';

Vil give en anden eksekveringsplan til:

SELECT *
FROM T
WHERE A = '20130123';

Da statistikken vil indikere, at de første 400 ud af 1.000 rækker vil blive returneret, bør optimeringsværktøjet erkende, at en tabelscanning vil være mere effektiv end et bogmærkeopslag, hvorimod den anden kun vil give 1 rækker, så et bogmærkeopslag vil være meget mere effektiv.

Tilbage til dit spørgsmål, hvis vi gjorde dette til en procedure:

CREATE PROCEDURE dbo.GetFromT @Param DATE
AS
    SELECT *
    FROM T
    WHERE A = @Param

Kør derefter

EXECUTE dbo.GetFromT '20130122'; --400 rows

Forespørgselsplanen med tabelscanningen vil blive brugt, hvis første gang du kører den, bruger du '20130123' som parameter, gemmer den bogmærkeopslagsplanen. Indtil de tidspunkter, hvor proceduren er genkompileret, vil planen forblive den samme. Gør noget som dette:

CREATE PROCEDURE dbo.GetFromT @Param VARCHAR(5)
AS
    DECLARE @Param2 VARCHAR(5) = @Param;
    SELECT *
    FROM T
    WHERE A = @Param2

Så køres dette:

EXECUTE dbo.GetFromT '20130122';

Selvom proceduren kompileres på én gang, flyder den ikke korrekt, så forespørgselsplanen, der blev oprettet ved den første kompilering, har ingen idé om, at @Param2 bliver det samme som @param, så optimeringsværktøjet (uden viden om, hvor mange rækker der skal forventer) antager, at 300 vil blive returneret (30%), da en tabelscanning vil være mere effektiv end et bogmærkeopslag. Hvis du kørte den samme procedure med '20130123' som parameter, ville det give den samme plan (uanset hvilken parameter den først blev aktiveret med), fordi statistikken ikke kan bruges til en ukendt værdi. Så det ville være mere effektivt at køre denne procedure for '20130122', men for alle andre værdier ville det være mindre effektivt end uden lokale parametre (forudsat at proceduren uden lokale parametre først blev påberåbt med alt andet end '20130122')

Nogle forespørgsler, der skal demonstreres, så du selv kan se udførelsesplaner

Opret skema og eksempeldata

CREATE TABLE T (ID INT IDENTITY(1, 1) PRIMARY KEY, A DATE NOT NULL, B INT,C INT, D INT, E INT);

CREATE NONCLUSTERED INDEX IX_T ON T (A);

INSERT T (A, B, C, D, E)
SELECT  TOP 400 CAST('20130122' AS DATE), number, 2, 3, 4 
FROM    Master..spt_values 
WHERE   type = 'P'
UNION ALL
SELECT TOP 600 DATEADD(DAY, number, CAST('20130122' AS DATE)), number, 2, 3, 4 
FROM    Master..spt_values 
WHERE   Type = 'P';
GO
CREATE PROCEDURE dbo.GetFromT @Param DATE
AS
    SELECT *
    FROM T
    WHERE A = @Param
GO
CREATE PROCEDURE dbo.GetFromT2 @Param DATE
AS
    DECLARE @Param2 DATE = @Param;
    SELECT *
    FROM T
    WHERE A = @Param2
GO

Kør procedurer (viser faktisk udførelsesplan):

EXECUTE GetFromT '20130122';
EXECUTE GetFromT '20130123';
EXECUTE GetFromT2 '20130122';
EXECUTE GetFromT2 '20130123';
GO
EXECUTE SP_RECOMPILE GetFromT;
EXECUTE SP_RECOMPILE GetFromT2;
GO
EXECUTE GetFromT '20130123';
EXECUTE GetFromT '20130122';
EXECUTE GetFromT2 '20130123';
EXECUTE GetFromT2 '20130122';

Du vil se det første gang GetFromT er kompileret bruger den en tabelscanning og beholder denne, når den køres med parameteren '20130122', GetFromT2 bruger også en tabelscanning og beholder planen for '20130122'.

Efter at procedurerne er blevet indstillet til rekompilering og kørt igen (bemærk i en anden rækkefølge), GetFromT bruger en bogmærke-loopup og beholder planen for '20130122', på trods af at man tidligere har vurderet, at en tabelscanning er en mere passende plan. GetFromT2 er upåvirket af ordren og har samme plan som før genoverholdelsen.

Så sammenfattende afhænger det af fordelingen af ​​dine data og dine indekser, din frekvens af rekompilering og en smule held med hensyn til, om en procedure vil have gavn af at bruge lokale variabler. Det gør det bestemt ikke altid hjælp.

Forhåbentlig har jeg kastet lidt lys over effekten af ​​at bruge lokale parametre, eksekveringsplaner og stored procedure compliation. Hvis jeg har fejlet fuldstændigt eller misset et nøglepunkt, kan en meget mere dybdegående forklaring findes her:

http://www.sommarskog.se/query-plan-mysteries.html



  1. MySQL højt CPU-forbrug

  2. MySQL til Visual Studio installation mislykkes, fejlkode 1603

  3. Indstillingsværdi for én kolonne af alle poster i tabellen

  4. Windows 8 og MySQL? Hvad er mine muligheder?