sql >> Databáze >  >> RDS >> Sqlserver

Pomáhá přiřazení vstupních parametrů uložené procedury lokálním proměnným optimalizovat dotaz?

Nebudu se pokoušet vysvětlovat úplné detaily sniffování parametrů, ale zkrátka ne vždy pomoci (a může překážet).

Představte si tabulku (T) s primárním klíčem a indexovaným sloupcem Datum (A), v tabulce je 1000 řádků, 400 má stejnou hodnotu A (řekněme dnes 20130122), zbývajících 600 řádků je dalších 600 dní , takže pouze 1 záznam na datum.

Tento dotaz:

SELECT *
FROM T
WHERE A = '20130122';

Výsledkem bude jiný plán provádění než:

SELECT *
FROM T
WHERE A = '20130123';

Protože statistiky naznačují, že pro prvních 400 z 1 000 řádků bude vráceno, měl by optimalizátor uznat, že skenování tabulky bude efektivnější než vyhledávání záložek, zatímco druhé poskytne pouze 1 řádek, takže vyhledávání záložek bude mnohem efektivnější.

Nyní zpět k vaší otázce, pokud jsme z toho udělali postup:

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

Pak spusťte

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

Použije se plán dotazů se skenováním tabulky, pokud při prvním spuštění použijete jako parametr '20130123', uloží se plán vyhledávání záložek. Dokud nebude postup překompilován, plán zůstane stejný. Dělat něco takového:

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

Poté se spustí toto:

EXECUTE dbo.GetFromT '20130122';

I když je procedura zkompilována najednou, neprobíhá správně, takže plán dotazů vytvořený při první kompilaci netuší, že @Param2 se stane stejným jako @param, takže optimalizátor (bez znalosti počtu řádků očekávat) bude předpokládat, že se vrátí 300 (30 %), protože takový bude považovat skenování tabulky za efektivnější než vyhledávání záložek. Pokud byste spustili stejnou proceduru s parametrem „20130123“, přineslo by to stejný plán (bez ohledu na to, s jakým parametrem byl poprvé vyvolán), protože statistiku nelze použít pro neznámou hodnotu. Spuštění této procedury pro „20130122“ by tedy bylo efektivnější, ale pro všechny ostatní hodnoty by bylo méně efektivní než bez lokálních parametrů (za předpokladu, že procedura bez lokálních parametrů byla nejprve vyvolána s čímkoli jiným než „20130122“)

Některé dotazy k demonstraci, abyste si sami mohli prohlédnout plány provádění

Vytvořte schéma a ukázková data

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

Spustit procedury (zobrazující skutečný plán provádění):

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

Uvidíte, že poprvé GetFromT je zkompilován, používá skenování tabulek a zachovává to při spuštění s parametrem '20130122', GetFromT2 také používá skenování tabulky a zachovává plán pro '20130122'.

Po nastavení procedur pro rekompilaci a opětovném spuštění (poznamenejte si v jiném pořadí), GetFromT používá smyčku záložek a zachovává plán pro '20130122', přestože se dříve domníval, že skenování tabulky je vhodnější plán. GetFromT2 není objednávkou ovlivněna a má stejný plán jako před kompliací.

Stručně řečeno, záleží na distribuci vašich dat a vašich indexů, frekvenci rekompilace a troše štěstí, zda bude procedura těžit z použití lokálních proměnných. Rozhodně to není vždy pomoc.

Doufám, že jsem trochu osvětlil účinek použití místních parametrů, plánů provádění a kompilace uložených procedur. Pokud jsem úplně selhal nebo jsem přehlédl klíčový bod, mnohem podrobnější vysvětlení lze nalézt zde:

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



  1. Chyba 28000:Přihlášení se nezdařilo pro uživatele DOMAIN\\user s pyodbc

  2. Jak zřetězit sloupce s Laravel 4 Eloquent?

  3. co je špatného na tomto dotazu Magento?

  4. Problém MySQL ODBC:Nebyl nalezen název zdroje dat a nebyl zadán žádný výchozí ovladač