sql >> Database >  >> RDS >> Sqlserver

Helpt het toewijzen van invoerparameters voor opgeslagen procedures aan lokale variabelen bij het optimaliseren van de query?

Ik zal niet proberen de volledige details van het snuiven van parameters uit te leggen, maar kortom, nee, dat is niet altijd helpen (en het kan hinderen).

Stel je een tabel (T) voor met een primaire sleutel en een geïndexeerde kolom Datum (A), in de tabel zijn er 1.000 rijen, 400 hebben dezelfde waarde van A (laten we zeggen vandaag 20130122), de resterende 600 rijen zijn de komende 600 dagen , dus slechts 1 record per datum.

Deze vraag:

SELECT *
FROM T
WHERE A = '20130122';

Zal een ander uitvoeringsplan opleveren voor:

SELECT *
FROM T
WHERE A = '20130123';

Aangezien de statistieken aangeven dat voor de eerste 400 van de 1.000 rijen worden geretourneerd, moet de optimizer erkennen dat een tabelscan efficiënter zal zijn dan een bladwijzerzoekopdracht, terwijl de tweede slechts 1 rij oplevert, dus een bladwijzerzoekopdracht zal veel efficiënter.

Nu, terug naar uw vraag, als we hier een procedure van hebben gemaakt:

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

Ren dan

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

Het queryplan met de tabelscan zal worden gebruikt, als u de eerste keer dat u het uitvoert '20130123' als parameter gebruikt, wordt het bladwijzeropzoekplan opgeslagen. Zolang de procedure niet opnieuw wordt gecompileerd, blijft het plan hetzelfde. Zoiets doen:

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

Dan wordt dit uitgevoerd:

EXECUTE dbo.GetFromT '20130122';

Hoewel de procedure in één keer wordt gecompileerd, verloopt deze niet goed, dus het queryplan dat bij de eerste compilatie is gemaakt, heeft geen idee dat @Param2 hetzelfde wordt als @param, dus de optimiser (zonder kennis van hoeveel rijen verwachten) gaat ervan uit dat 300 wordt geretourneerd (30%), daarom wordt een tabelscan efficiënter geacht dan het opzoeken van een bladwijzer. Als u dezelfde procedure zou uitvoeren met '20130123' als parameter, zou dit hetzelfde plan opleveren (ongeacht met welke parameter het voor het eerst werd aangeroepen) omdat de statistieken niet kunnen worden gebruikt voor een onbekende waarde. Dus het uitvoeren van deze procedure voor '20130122' zou efficiënter zijn, maar voor alle andere waarden zou het minder efficiënt zijn dan zonder lokale parameters (ervan uitgaande dat de procedure zonder lokale parameters eerst werd aangeroepen met alles behalve '20130122')

Enkele vragen om aan te tonen, zodat u de uitvoeringsplannen zelf kunt bekijken

Schema en voorbeeldgegevens maken

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

Procedures uitvoeren (met het daadwerkelijke uitvoeringsplan):

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';

Je zult zien dat de eerste keer GetFromT is gecompileerd, gebruikt een tabelscan en behoudt deze wanneer uitgevoerd met de parameter '20130122', GetFromT2 maakt ook gebruik van een tabelscan en behoudt het plan voor '20130122'.

Nadat de procedures voor hercompilatie zijn ingesteld en opnieuw worden uitgevoerd (let op in een andere volgorde), GetFromT gebruikt een bladwijzer-loopup en behoudt het plan voor '20130122', ondanks het feit dat eerder werd aangenomen dat een tabelscan een geschikter plan is. GetFromT2 wordt niet beïnvloed door de bestelling en heeft hetzelfde plan als vóór de hercompliateion.

Samenvattend hangt het dus af van de distributie van uw gegevens en uw indexen, uw frequentie van hercompilatie en een beetje geluk of een procedure baat zal hebben bij het gebruik van lokale variabelen. Het is zeker niet altijd hulp.

Hopelijk heb ik enig licht geworpen op het effect van het gebruik van lokale parameters, uitvoeringsplannen en het compileren van opgeslagen procedures. Als ik volledig heb gefaald, of een belangrijk punt heb gemist, vind je hier een veel uitgebreidere uitleg:

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



  1. Ondersteunt Microsoft OLE DB Provider voor SQL Server TLS 1.2?

  2. Waarom zweeft MySQL round veel meer dan verwacht?

  3. De versie van SQL Server die in gebruik is, ondersteunt het datatype datetime2 niet?

  4. Oracle-foutmelding invoerwaarde niet lang genoeg