it-swarm-eu.dev

Plán provedení ukazuje nákladnou operaci CONVERT_IMPLICIT. Mohu to opravit indexováním nebo musím změnit tabulku?

Mám opravdu důležitý, opravdu pomalý pohled, který obsahuje některé opravdu ošklivé podmínky, jako je tento, v klauzuli where. Jsem si také vědom toho, že spojení jsou hrubá a pomalá spojení namísto celých identifikačních polí v varchar(13), ale rád bych vylepšil níže uvedený jednoduchý dotaz, který používá toto zobrazení:

CREATE VIEW [dbo].[vwReallySlowView]  AS  
AS  
SELECT     
  I.booking_no_v32 AS bkno, 
  I.trans_type_v41 AS trantype, 
  B.Assigned_to_v61 AS Assignbk, 
  B.order_date AS dateo, B.HourBooked AS HBooked,   
  B.MinBooked AS MBooked, B.SecBooked AS SBooked, 
  I.prep_on AS Pon, I.From_locn AS Flocn, 
  I.Trans_to_locn AS TTlocn,   
                      (CASE I.prep_on WHEN 'Y' THEN I.PDate ELSE I.FirstDate END) AS PrDate, I.PTimeH AS PrTimeH, I.PTimeM AS PrTimeM,   
                      (CASE WHEN I.RetnDate < I.FirstDate THEN I.FirstDate ELSE I.RetnDate END) AS RDatev, I.bit_field_v41 AS bitField, I.FirstDate AS FDatev, I.BookDate AS DBooked,   
                      I.TimeBookedH AS TBookH, I.TimeBookedM AS TBookM, I.TimeBookedS AS TBookS, I.del_time_hour AS dth, I.del_time_min AS dtm, I.return_to_locn AS rtlocn,   
                      I.return_time_hour AS rth, I.return_time_min AS rtm, (CASE WHEN I.Trans_type_v41 IN (6, 7) AND (I.Trans_qty < I.QtyCheckedOut)   
                      THEN 0 WHEN I.Trans_type_v41 IN (6, 7) AND (I.Trans_qty >= I.QtyCheckedOut) THEN I.Trans_Qty - I.QtyCheckedOut ELSE I.trans_qty END) AS trqty,   
                      (CASE WHEN I.Trans_type_v41 IN (6, 7) THEN 0 ELSE I.QtyCheckedOut END) AS MyQtycheckedout, (CASE WHEN I.Trans_type_v41 IN (6, 7)   
                      THEN 0 ELSE I.QtyReturned END) AS retqty, I.ID, B.BookingProgressStatus AS bkProg, I.product_code_v42, I.return_to_locn, I.AssignTo, I.AssignType,   
                      I.QtyReserved, B.DeprepOn,  
        (CASE  B.DeprepOn       
        WHEN 1 THEN  B.DeprepDateTime     
        ELSE   I.RetnDate  
           END)  AS DeprepDateTime, I.InRack 
FROM         dbo.tblItemtran AS I 

INNER JOIN  -- booking_no = varchar(13)
         dbo.tblbookings AS B ON B.booking_no = I.booking_no_v32  --  string inner-join

INNER JOIN  -- product_code = varchar(13) 
        dbo.tblInvmas AS M ON I.product_code_v42 = M.product_code  --  string inner-join

WHERE     (I.trans_type_v41 NOT IN (2, 3, 7, 18, 19, 20, 21, 12, 13, 22)) AND (I.trans_type_v41 NOT IN (6, 7)) AND (I.bit_field_v41 & 4 = 0) OR  
                      (I.trans_type_v41 NOT IN (6, 7)) AND (I.bit_field_v41 & 4 = 0) AND (B.BookingProgressStatus = 1) OR  
                      (I.trans_type_v41 IN (6, 7)) AND (I.bit_field_v41 & 4 = 0) AND (I.QtyCheckedOut = 0) OR  
                      (I.trans_type_v41 IN (6, 7)) AND (I.bit_field_v41 & 4 = 0) AND (I.QtyCheckedOut > 0) AND (I.trans_qty - (I.QtyCheckedOut - I.QtyReturned) > 0)  

Toto zobrazení se obvykle používá takto:

select * from vwReallySlowView
where product_code_v42  = 'LIGHTBULB100W'  -- find "100 watt lightbulb" rows

Když to spustím, dostanu tuto položku plánu provádění, která stojí 20 až 80% celkových nákladů na dávku, s predikátem CONVERT_IMPLICIT( .... &(4)), který ukazuje, že se zdá být velmi pomalým při provádění těchto bitwise boolean tests Jako (I.ibitfield & 4 = 0).

Nejsem odborník na práci v MS SQL nebo na typu DBA obecně, protože jsem většinou vývojář softwaru, který není SQL. Mám ale podezření, že takové bitové kombinace jsou špatný nápad a že by bylo lepší mít diskrétní booleovská pole.

Mohl bych nějak zlepšit tento index, který mám, abych lépe zvládl tento pohled beze změny schématu (které je již v produkci na tisících místech), nebo musím změnit podkladovou tabulku, která má několik booleovských hodnot zabalených do celého čísla bit_field_v41, opravit tento problém?

Zde je můj Clustered Index na tblItemtran, který je skenován v tomto plánu provádění:

-- goal:  speed up  select * from vwReallySlowView where productcode  = 'X'
CREATE CLUSTERED INDEX [idxtblItemTranProductCodeAndTransType] ON [dbo].[tblItemtran] 
(
    [product_code_v42] ASC,  -- varchar(13)
    [trans_type_v41] ASC     -- int
)WITH ( PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, 
        IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, 
        ALLOW_PAGE_LOCKS  = ON) 
ON [PRIMARY]

Zde je plán provedení jednoho z dalších produktů, který má za následek 27% náklady na tento predikát CONVERT_IMPLICIT. aktualizace Všimněte si, že v tomto případě je mým nejhorším uzlem „hash match“ na inner join, což stojí 34% I věřte, že to je cena, které se nemohu vyhnout, pokud se nedokážu vyhnout spojení na strunách, které se v současné době nemohu zbavit. Obě operace INNER JOIN Ve výše uvedeném pohledu jsou na polích varchar(13).

Přiblížení v pravém dolním rohu:

enter image description here

Celý plán provádění jako .sqlplan je k dispozici na skydrive. Tento obrázek je pouze vizuální přehled. Kliknutím na zde zobrazíte samotný obrázek.

enter image description here

Aktualizace zveřejnila celý plán provádění. Nezdá se mi, že bych zjistil, jaká hodnota product_code Byla patologicky špatná, ale jedním ze způsobů, jak toho dosáhnout, je select count(*) from view místo toho, abych dělal jediný produkt. Zdá se však, že produkty, které se používají pouze v 5% záznamů v podkladové tabulce nebo méně, vykazují při operaci CONVERT_IMPLICIT Mnohem nižší náklady. Kdybych tu chtěl opravit SQL, myslím, že bych si vzal hrubou klauzuli WHERE do pohledu a vypočítal a uložil výsledek této obří podmínky where-Klaus jako bit "IncludeMeInTheView" v podkladové tabulce. Presto, problém vyřešen, že?

24
Warren P

V plánech provádění byste se neměli příliš spoléhat na procenta nákladů. Jedná se o vždy odhadované náklady, a to i v plánech po provedení s „skutečnými“ čísly pro věci, jako je počet řádků. Odhadované náklady jsou založeny na modelu, který funguje velmi dobře pro účel, pro který je určen: umožnění optimalizátoru zvolit mezi různými plány provádění kandidátů pro stejný dotaz. Informace o nákladech jsou zajímavé a faktor, který je třeba zvážit, ale zřídka by měla být primární metrikou pro ladění dotazů. Interpretace informací plánu provádění vyžaduje širší pohled na prezentovaná data.

ItemTran Clustered Index Seek Operator

ItemTran Clustered Index Seek

Tento operátor jsou skutečně dvě operace v jednom. Nejprve operace vyhledávání indexu najde všechny řádky, které odpovídají predikátu product_code_v42 = 'M10BOLT', Pak každý řádek použije zbytkový predikát bit_field_v41 & 4 = 0. Dochází k implicitní konverzi bit_field_v41 Z jejího základního typu (tinyint nebo smallint) na integer.

K převodu dochází, protože bitový operátor AND (&) vyžaduje, aby oba operandy byly stejného typu. Implicitní typ konstantní hodnoty '4' je celé číslo a pravidla priority datového typ znamená, že je převedena hodnota pole bit_field_v41 S nižší prioritou.

Problém (jako je) se snadno opraví zápisem predikátu jako bit_field_v41 & CONVERT(tinyint, 4) = 0 - což znamená, že konstantní hodnota má nižší prioritu a je převedena (během konstantního skládání) spíše než hodnota sloupce. Pokud je bit_field_v41tinyint, nedojde k žádné konverzi. Podobně lze použít funkci CONVERT(smallint, 4), pokud bit_field_v41 Je smallint. To znamená, že v tomto případě převod není problém s výkonem, ale stále je dobrým zvykem porovnávat typy a vyhýbat se implicitním převodům tam, kde je to možné.

Hlavní část odhadovaných nákladů na toto hledání je až do velikosti základní tabulky. Zatímco seskupený klíč indexu je sám o sobě poměrně úzký, velikost každého řádku je velká. Definice tabulky není uvedena, ale pouze sloupce použité v pohledu přidávají významnou šířku řádku. Protože seskupený index zahrnuje všechny sloupce, vzdálenost mezi seskupenými klíči indexu je šířka řádek, nikoli šířka indexové klíče. Použití přípon přípon verzí v některých sloupcích naznačuje, že skutečná tabulka obsahuje ještě více sloupců pro předchozí verze.

Když se podíváme na sloupce vyhledávání, zbytkového predikátu a výstupu, výkon tohoto operátora lze zkontrolovat izolovaně vytvořením ekvivalentního dotazu (1 <> 2 Je trik, který zabraňuje auto-parametrizaci, rozpor je odstraněn optimalizátorem a nezobrazí se v plánu dotazů):

SELECT
    it.booking_no_v32,
    it.QtyCheckedOut,
    it.QtyReturned,
    it.Trans_qty,
    it.trans_type_v41
FROM dbo.tblItemTran AS it
WHERE
    1 <> 2
    AND it.product_code_v42 = 'M10BOLT'
    AND it.bit_field_v41 & CONVERT(tinyint, 4) = 0;

Výkon tohoto dotazu se studenou datovou mezipamětí je zajímavý, protože čtení (forward) by bylo ovlivněno fragmentací tabulky (seskupený index). Klastrovací klíč pro tuto tabulku vyzývá k fragmentaci, takže by mohlo být důležité tento index pravidelně udržovat (reorganizovat nebo znovu sestavovat) a pomocí vhodného FILLFACTOR umožnit místo pro nové řádky mezi okny údržby indexů.

Provedl jsem test účinku fragmentace na čtení předem pomocí vzorových dat generovaných pomocí SQL Data Generator . Při použití stejného počtu řádků tabulky, jaké jsou uvedeny v plánu dotazů na otázku, vyústil vysoce fragmentovaný klastrovaný index do SELECT * FROM view Po 15 sekundách po DBCC DROPCLEANBUFFERS. Stejný test za stejných podmínek s čerstvě obnoveným seskupeným indexem v tabulce ItemTrans byl dokončen za 3 sekundy.

Pokud jsou data tabulky obvykle zcela v mezipaměti, je problém fragmentace mnohem méně důležitý. Ale i při nízké fragmentaci mohou široké řádky tabulky znamenat, že počet logických a fyzických čtení je mnohem vyšší, než by se dalo očekávat. Můžete také experimentovat s přidáním a odebráním explicitního CONVERT, abyste potvrdili mé očekávání, že implicitní problém s převodem zde není důležitý, s výjimkou porušení doporučených postupů.

Více k tomuto bodu je odhadovaný počet řádků opouštějících operátora vyhledávání. Odhad času optimalizace je 165 řádků, ale v době provedení bylo vyrobeno 4 226 kusů. K tomuto bodu se vracím později, ale hlavním důvodem nesrovnalosti je to, že selektivita zbytkového predikátu (zahrnující bitový-AND) je pro optimalizátor velmi obtížná předvídat - ve skutečnosti se uchyluje k hádání.

Operátor filtru

Filter Operator

Zobrazuji zde predikát filtru většinou, abych ilustroval, jak jsou dva seznamy NOT IN Kombinovány, zjednodušeny a poté rozšířeny, a také poskytuji odkaz pro následující diskusi o hašovacích zápasech. Testovací dotaz z požadavku lze rozšířit tak, aby zahrnoval jeho účinky a určoval vliv operátora filtru na výkon:

SELECT
    it.booking_no_v32,
    it.trans_type_v41,
    it.Trans_qty,
    it.QtyReturned,
    it.QtyCheckedOut
FROM dbo.tblItemTran AS it
WHERE
    it.product_code_v42 = 'M10BOLT'
    AND it.bit_field_v41 & CONVERT(tinyint, 4) = 0
    AND
    (
        (
            it.trans_type_v41 NOT IN (2, 3, 6, 7, 18, 19, 20, 21, 12, 13, 22)
            AND it.trans_type_v41 NOT IN (6, 7)
        )
        OR
        (
            it.trans_type_v41 NOT IN (6, 7)
        )
        OR 
        (
            it.trans_type_v41 IN (6, 7)
            AND it.QtyCheckedOut = 0
        )
        OR 
        (
            it.trans_type_v41 IN (6, 7)
            AND it.QtyCheckedOut > 0
            AND it.trans_qty - (it.QtyCheckedOut - it.QtyReturned) > 0
        )
    );

Operátor Compute Scalar v plánu definuje následující výraz (samotný výpočet je odložen, dokud není výsledek vyžadován pozdějším operátorem):

[Expr1016] = (trans_qty - (QtyCheckedOut - QtyReturned))

Hash Match Operator

Provádění spojení na datových typech znaků není důvodem vysokých odhadovaných nákladů tohoto operátora. Popis SSMS zobrazuje pouze položku Hash Keys Probe Probe, ale důležité detaily jsou v okně SSMS Properties.

Operátor Hash Match vytvoří hašovací tabulku s použitím hodnot ve sloupci booking_no_v32 (Build Hash Keys!) Z tabulky ItemTran a poté zkoumá shody pomocí sloupce booking_no (Hash Keys Probe) od tabulka rezervace. Popisek SSMS by také normálně zobrazoval zbytky sondy, ale text je příliš dlouhý na popis a je jednoduše vynechán.

Rezidua sondy je podobná zbytku pozorovanému po hledání indexu dříve; zbytkový predikát je vyhodnocen na všech řádcích, které hash odpovídají, aby se určilo, zda by měl být řádek předán nadřazenému operátorovi. Nalezení hash zápasů v dobře vyvážené hashovací tabulce je extrémně rychlé, ale použití komplexního zbytkového predikátu na každý řádek, který odpovídá, je při porovnání poměrně pomalé. Popisek Hash Match v aplikaci Plan Explorer zobrazuje podrobnosti, včetně zbytkového výrazu Probe:

Hash Match Operator

Zbytkový predikát je složitý a zahrnuje kontrolu stavu průběhu rezervace, když je sloupec k dispozici v tabulce rezervací. Popisek také ukazuje stejný nesoulad mezi odhadovanými a skutečnými počty řádků viděnými dříve v hledání indexu. Může se zdát podivné, že většina filtrování se provádí dvakrát, ale je to jen optimistický optimalizátor. Neočekává, že části filtru, které lze posunout dolů ze zbytku sondy, odstraní všechny řádky (odhady počtu řádků jsou před a za filtrem stejné), ale optimalizátor ví, že by to mohlo být špatné. Pravděpodobnost brzkého filtrování řádků (snížení nákladů na spojení hash) stojí za malou cenu extra filtru. Celý filtr nelze posunout dolů, protože obsahuje test na sloupci z tabulky rezervací, ale většinou to může být. Úplný test je stále vyžadován v testu zbytkové hodnoty hash pro správnost.

Podceňování počtu řádků je problém pro operátora Hash Match, protože množství paměti rezervované pro hashovací tabulku je založeno na odhadovaném počtu řádků. Pokud je paměť příliš malá pro velikost hashovací tabulky požadované v době běhu (kvůli většímu počtu řádků), hashova tabulka rekurzivně rozlévá na fyzickou tempdb úložiště, což často vede k velmi slabému výkonu. V nejhorším případě prováděcí motor zastaví rekurzivní rozlití hashovacích kbelíků a uchytí se k algoritmu velmi pomalý Rozlití hash (rekurzivní nebo záchranná opatření) je nejpravděpodobnější příčinou problémů s výkonem nastíněných v otázce (nikoli sloupců spojení typu nebo implicitních konverzí). Hlavní příčinou by byl server rezervující příliš málo paměti pro dotaz na základě nesprávného odhadu počtu řádků (kardinality).

Je smutné, že před SQL Server 2012 není v plánu provádění naznačeno, že operace hašování překročila přidělení paměti (které nemůže být dynamicky růst po rezervování před spuštěním spuštění, i když má server velké množství volné paměti) a musela se přelít do tempdb. Je možné monitorovat Hash Warning Event Class pomocí Profiler, ale může být obtížné korelovat varování s konkrétním dotazem.

Oprava problémů

Těmito třemi problémy jsou fragmentace, zbytková složka sondy v operátorovi hašovací shody a nesprávný odhad mohutnosti vyplývající z hádání při hledání indexu.

Doporučené řešení

Zkontrolujte fragmentaci a v případě potřeby ji opravte, naplánujte údržbu tak, aby index zůstal přijatelně organizovaný. Obvyklý způsob, jak opravit odhad kardinality, je poskytnout statistiku. V takovém případě optimalizátor potřebuje statistiku kombinace (product_code_v42, bitfield_v41 & 4 = 0). Nemůžeme vytvořit statistiky výrazu přímo, takže musíme nejprve vytvořit vypočítaný sloupec pro výraz bitového pole a poté vytvořit manuální statistiku více sloupců:

ALTER TABLE dbo.tblItemTran
ADD Bit3 AS bit_field_v41 & CONVERT(tinyint, 4);

CREATE STATISTICS [stats dbo.ItemTran (product_code_v42, Bit3)]
ON dbo.tblItemTran (product_code_v42, Bit3);

Definice vypočteného sloupcového textu se musí přesně shodovat s textem v definici pohledu, aby se statistika mohla použít, takže by se mělo korigovat zobrazení, aby se vyloučila implicitní konverze, a současně by se mělo dbát na to, aby byla zajištěna shoda textu.

Statistiky ve více sloupcích by měly vést k mnohem lepším odhadům, což výrazně snižuje pravděpodobnost, že operátor hashovací metody použije rekurzivní rozlití nebo algoritmus výpomoci. Přidání vypočítaného sloupce (což je operace pouze s metadaty a nezabere žádné místo v tabulce, protože není označeno PERSISTED) a statistika více sloupců je můj nejlepší odhad při prvním řešení.

Při řešení problémů s výkonem dotazů je důležité měřit věci jako uplynulý čas, využití procesoru, logická čtení, fyzická čtení, typy čekání a doby trvání atd. Atd. Může být také užitečné spustit části dotazu samostatně pro ověření podezření na příčiny, jak je uvedeno výše.

V některých prostředích, kde není nejdůležitější pohled na data, může být užitečné spustit proces na pozadí, který zhmotňuje celý pohled do tabulky snímků každou tak často. Tato tabulka je pouze normální základní tabulka a lze ji indexovat pro čtení dotazů, aniž byste se museli starat o dopad na výkon aktualizace.

Zobrazit indexování

Nenechte se v pokušení přímo indexovat původní pohled. Výkon čtení bude úžasně rychlý (jediné hledání v pohledu indexu), ale (v tomto případě) budou všechny problémy s výkonem ve stávajících plánech dotazů přeneseny do dotazů, které upravují všechny sloupce tabulky, na které se v pohledu odkazuje. Dotazy, které mění řádky základní tabulky, budou skutečně ovlivněny velmi špatně.

Pokročilé řešení s částečným indexovaným pohledem

Pro tento konkrétní dotaz existuje částečné řešení indexovaného pohledu, které opravuje odhady kardinality a odstraňuje zbytky filtru a sondy, ale je založeno na některých předpokladech o datech (většinou můj odhad na schématu) a vyžaduje odbornou implementaci, zejména pokud jde o vhodné indexy pro podporu plánů údržby indexovaných pohledů. Sdílím níže uvedený kód pro zájem, nenavrhuji, abyste jej implementovali bez velmi opatrní analýzy a testování.

-- Indexed view to optimize the main view
CREATE VIEW dbo.V1
WITH SCHEMABINDING
AS
SELECT
    it.ID,
    it.product_code_v42,
    it.trans_type_v41,
    it.booking_no_v32,
    it.Trans_qty,
    it.QtyReturned,
    it.QtyCheckedOut,
    it.QtyReserved,
    it.bit_field_v41,
    it.prep_on,
    it.From_locn,
    it.Trans_to_locn,
    it.PDate,
    it.FirstDate,
    it.PTimeH,
    it.PTimeM,
    it.RetnDate,
    it.BookDate,
    it.TimeBookedH,
    it.TimeBookedM,
    it.TimeBookedS,
    it.del_time_hour,
    it.del_time_min,
    it.return_to_locn,
    it.return_time_hour,
    it.return_time_min,
    it.AssignTo,
    it.AssignType,
    it.InRack
FROM dbo.tblItemTran AS it
JOIN dbo.tblBookings AS tb ON
    tb.booking_no = it.booking_no_v32
WHERE
    (
        it.trans_type_v41 NOT IN (2, 3, 7, 18, 19, 20, 21, 12, 13, 22)
        AND it.trans_type_v41 NOT IN (6, 7)
        AND it.bit_field_v41 & CONVERT(tinyint, 4) = 0
    )
    OR
    (
        it.trans_type_v41 NOT IN (6, 7)
        AND it.bit_field_v41 & CONVERT(tinyint, 4) = 0
        AND tb.BookingProgressStatus = 1
    )
    OR 
    (
        it.trans_type_v41 IN (6, 7)
        AND it.bit_field_v41 & CONVERT(tinyint, 4) = 0
        AND it.QtyCheckedOut = 0
    )
    OR 
    (
        it.trans_type_v41 IN (6, 7)
        AND it.bit_field_v41 & CONVERT(tinyint, 4) = 0
        AND it.QtyCheckedOut > 0
        AND it.trans_qty - (it.QtyCheckedOut - it.QtyReturned) > 0
    );
GO
CREATE UNIQUE CLUSTERED INDEX cuq ON dbo.V1 (product_code_v42, ID);
GO

Existující pohled se vyladil, aby používal indexované zobrazení výše:

CREATE VIEW [dbo].[vwReallySlowView2]
AS
SELECT
    I.booking_no_v32 AS bkno,
    I.trans_type_v41 AS trantype,
    B.Assigned_to_v61 AS Assignbk,
    B.order_date AS dateo,
    B.HourBooked AS HBooked,
    B.MinBooked AS MBooked,
    B.SecBooked AS SBooked,
    I.prep_on AS Pon,
    I.From_locn AS Flocn,
    I.Trans_to_locn AS TTlocn,
    CASE I.prep_on 
        WHEN 'Y' THEN I.PDate
        ELSE I.FirstDate
    END AS PrDate,
    I.PTimeH AS PrTimeH,
    I.PTimeM AS PrTimeM,
    CASE
        WHEN I.RetnDate < I.FirstDate 
        THEN I.FirstDate 
        ELSE I.RetnDate
    END AS RDatev,
    I.bit_field_v41 AS bitField,
    I.FirstDate AS FDatev,
    I.BookDate AS DBooked,
    I.TimeBookedH AS TBookH,
    I.TimeBookedM AS TBookM,
    I.TimeBookedS AS TBookS,
    I.del_time_hour AS dth,
    I.del_time_min AS dtm,
    I.return_to_locn AS rtlocn,
    I.return_time_hour AS rth,
    I.return_time_min AS rtm,
    CASE
        WHEN
            I.Trans_type_v41 IN (6, 7) 
            AND I.Trans_qty < I.QtyCheckedOut
            THEN 0 
        WHEN
            I.Trans_type_v41 IN (6, 7)
            AND I.Trans_qty >= I.QtyCheckedOut
            THEN I.Trans_Qty - I.QtyCheckedOut
        ELSE
            I.trans_qty
    END AS trqty,
    CASE
        WHEN I.Trans_type_v41 IN (6, 7)
        THEN 0
        ELSE I.QtyCheckedOut
    END AS MyQtycheckedout,
    CASE
        WHEN I.Trans_type_v41 IN (6, 7)
        THEN 0
        ELSE I.QtyReturned
    END AS retqty,
    I.ID,
    B.BookingProgressStatus AS bkProg,
    I.product_code_v42,
    I.return_to_locn,
    I.AssignTo,
    I.AssignType,
    I.QtyReserved,
    B.DeprepOn,
    CASE B.DeprepOn
        WHEN 1 THEN B.DeprepDateTime
        ELSE I.RetnDate
    END AS DeprepDateTime,
    I.InRack
FROM dbo.V1 AS I WITH (NOEXPAND)
JOIN dbo.tblbookings AS B ON
    B.booking_no = I.booking_no_v32
JOIN dbo.tblInvmas AS M ON
    I.product_code_v42 = M.product_code;

Příklad dotazu a plán provedení:

SELECT
    vrsv.*
FROM dbo.vwReallySlowView2 AS vrsv
WHERE vrsv.product_code_v42 = 'M10BOLT';

New execution plan

V novém plánu má hash shoda žádný zbytkový predikát, existuje žádný složitý filtr, žádný zbytkový predikát zapnuto hledat indexované zobrazení a odhady kardinality jsou přesně správné.

Jako příklad toho, jak by byly ovlivněny plány vložení/aktualizace/odstranění, je to plán vložení do tabulky ItemTrans:

Insert plan

Zvýrazněná část je nová a vyžaduje se pro údržbu indexovaného pohledu. Cívka tabulky nahradí vložené řádky základních tabulek pro údržbu indexovaného pohledu. Každý řádek je spojen s tabulkou rezervací pomocí hledání seskupeného indexu, poté filtr použije složité predikáty klauzule WHERE, aby se zjistilo, zda je třeba řádek přidat do pohledu. Pokud ano, provede se vložení do seskupeného indexu pohledu.

Stejný test SELECT * FROM view Provedený dříve byl dokončen za 150 ms s indexovaným pohledem na místě.

Poslední věc: Všiml jsem si, že váš server 2008 R2 je stále v RTM. Neopraví to vaše problémy s výkonem, ale Service Pack 2 pro 2008 R2 je k dispozici od července 2012 a existuje mnoho dobrých důvodů, jak udržet co nejaktuálnější s aktualizacemi Service Pack.

49
Paul White 9