Občas vidím, že se lidé snaží „optimalizovat“ své aktualizační příkazy, aby se vyhnuli zápisu stejné hodnoty do určitého sloupce. Vždy jsem to chápal tak, že pokud se chystáte aktualizovat řádek, za předpokladu, že všechny hodnoty jsou v řadě, náklady na zamknutí řádku jsou mnohem vyšší než přírůstkové náklady na aktualizaci jednoho, dvou nebo všech sloupců v tomto řádku. .
Vytvořil jsem tedy jednoduchou tabulku, abych to otestoval:
CREATE TABLE dbo.whatever( ID INT IDENTITY(1,1) PRIMÁRNÍ KLÍČ, v1 NVARCHAR(50) NOT NULL, v2 NVARCHAR(50) NOT NULL, v3 NVARCHAR(50) NOT NULL, v4 NVARCHAR(50) NOT NULL, v5 NVARCHAR(50) NOT NULL, v6 NVARCHAR(50) NOT NULL);
Potom jsem vytvořil uloženou proceduru k naplnění tabulky 50 000 řádky různými malými řetězci:
CREATE PROCEDURE dbo.cleanASBEGIN SET NOCOUNT ON; TRUNCATE TABLE dbo.whatever;;WITH x(d) AS ( SELECT d FROM ( VALUES (N'abc'),(N'def'),(N'ghi'), (N'jkl'),(N'mno'),(N 'pqr') ) JAKO y(d) ) INSERT dbo.whatever(v1, v2, v3, v4, v5, v6) VYBRAT TOP (50000) x1.d, x2.d, x3.d, x4.d, x5 .d, x6.d FROM x AS x1, x AS x2, x AS x3, x AS x4, x AS x5, x AS x6, x AS x7;ENDGO
Pak jsem napsal aktualizační příkazy formulované dvěma způsoby, které byste se mohli "vyhnout" zápisu do konkrétního sloupce, vzhledem k tomuto přiřazení proměnné:
DECLARE @v1 NVARCHAR(50) =N'abc', @v2 NVARCHAR(50) =N'def', @v3 NVARCHAR(50) =N'ghi', @v4 NVARCHAR(50) =N'jkl ', @v5 NVARCHAR(50) =N'mno', @v6 NVARCHAR(50) =N'pqr';
Nejprve pomocí výrazu CASE zkontrolujte, zda je hodnota ve sloupci stejná jako hodnota v proměnné:
UPDATE dbo.whatever SET v1 =CASE WHEN v1 <> @v1 THEN @v1 ELSE v1 END, v2 =CASE WHEN v2 <> @v2 THEN @v2 ELSE v2 END, v3 =CASE WHEN v3 <> @v3 THEN @v3 ELSE v3 END, v4 =CASE WHEN v4 <> @v4 THEN @v4 ELSE v4 END, v5 =CASE WHEN v5 <> @v5 THEN @v5 ELSE v5 END, v6 =CASE WHEN v6 <> @v6 THEN @v6 ELSE v6 ENDWHERE( v1 <> @v1 OR v2 <> @v2 OR v3 <> @v3 OR v4 <> @v4 OR v5 <> @v5 OR v6 <> @v6);
A za druhé vydáním nezávislé UPDATE pro každý sloupec (každý cílí pouze na řádky, kde se tato hodnota ve skutečnosti změnila):
AKTUALIZOVAT dbo.whatever SET v1 =@v1 WHERE v1 <> @v1;AKTUALIZOVAT dbo.whatever SET v2 =@v2 WHERE v2 <> @v2;AKTUALIZOVAT dbo.whatever SET v3 =@v3 WHERE v3 <> @v3;AKTUALIZOVAT dbo.whatever SET v4 =@v4 KDE v4 <> @v4;AKTUALIZOVAT dbo.whatever SET v5 =@v5 WHERE v5 <> @v5;AKTUALIZOVAT dbo.whatever SET v6 =@v6 WHERE v6 <> @v6;Pak bych to přirovnal ke způsobu, jakým by to udělala většina z nás dnes:stačí AKTUALIZOVAT všechny sloupce, aniž by se zajímalo, zda to byla již existující hodnota pro tento konkrétní sloupec:
AKTUALIZACE dbo.whatever SET v1 =@v1, v2 =@v2, v3 =@v3, v4 =@v4, v5 =@v5, v6 =@v6WHERE( v1 <> @v1 OR v2 <> @v2 NEBO v3 <> @v3 OR v4 <> @v4 OR v5 <> @v5 OR v6 <> @v6);(Všechny tyto předpoklady předpokládají, že sloupce a parametry/proměnné nemají hodnotu NULL – museli by použít COALESCE k zohlednění porovnání hodnot NULL na obou stranách, pokud tomu tak je. Také předpokládají, že byste měli další klauzuli WHERE cílit na konkrétní řádky – v tomto příkladu můžete spustit první a třetí dotaz bez všeobjímající klauzule WHERE a zobrazit téměř identické výsledky. Pro stručnost jsem to nechal jednoduché.)
Pak jsem chtěl vidět, co se stane v těchto třech případech, kdy se může změnit jakákoli hodnota, kdy se mohou změnit konkrétní hodnoty, kdy se nezmění žádné hodnoty a kdy se změní všechny hodnoty. Mohl bych to ovlivnit změnou uložené procedury tak, aby vkládala konstanty do konkrétních sloupců, nebo změnou způsobu přiřazování proměnných.
-- k zobrazení, kdy se může jakákoli hodnota v řádku změnit, postup používá úplné křížové spojení:SELECT TOP (50000) x1.d, x2.d, x3.d, x4.d, x5.d, x6 .d -- abychom ukázali, kdy se konkrétní hodnoty změní na mnoha řádcích, můžeme napevno zakódovat konstanty:-- dvě hodnoty vyjmuty:SELECT TOP (50000) N'abc', N'def', x3.d, x4.d , x5.d, x6.d -- čtyři hodnoty vyňaté:SELECT TOP (50000) N'abc', N'def', N'ghi', N'jkl', x5.d, x6.d -- zobrazit když se žádné hodnoty nezmění, pevně zakódujeme všech šest hodnot:SELECT TOP (50000) N'abc', N'def', N'ghi', N'jkl', N'mno', N'pqr' -- a aby bylo vidět, kdy se všechny hodnoty změní, bylo by provedeno jiné přiřazení proměnných:DECLARE @v1 NVARCHAR(50) =N'zzz', @v2 NVARCHAR(50) =N'zzz', @v3 NVARCHAR(50) =N 'zzz', @v4 NVARCHAR(50) =N'zzz', @v5 NVARCHAR(50) =N'zzz', @v6 NVARCHAR(50) =N'zzz';Výsledky
Po provedení těchto testů zvítězila „aktualizace naslepo“ v každém jednotlivém scénáři. Teď si říkáte, co je to pár set milisekund? Odvozovat. Pokud ve svém systému provádíte mnoho aktualizací, může to začít skutečně vybírat daň.
Podrobné výsledky v Průzkumníku plánů:Jakákoli změna | 2 hodnoty osvobozeny | 4 hodnoty osvobozeny | Všechny hodnoty osvobozeny | Všechny změny
Na základě zpětné vazby od Roji jsem se rozhodl otestovat to také s několika indexy:
VYTVOŘIT INDEX x1 NA dbo.whatever(v1);VYTVOŘIT INDEX x2 NA dbo.whatever(v2);VYTVOŘIT INDEX x3 NA dbo.whatever(v3) INCLUDE(v4,v5,v6);Durace byly podstatně zvýšeny s těmito indexy:
Podrobné výsledky v Průzkumníku plánů:Jakákoli změna | 2 hodnoty osvobozeny | 4 hodnoty osvobozeny | Všechny hodnoty osvobozeny | Všechny změny
Závěr
Z tohoto testu se mi zdá, že většinou nemá cenu kontrolovat, zda se má nějaká hodnota aktualizovat. Pokud váš příkaz UPDATE ovlivňuje více sloupců, je pro vás téměř vždy levnější skenovat všechny sloupce, kde se mohla změnit jakákoli hodnota, než kontrolovat každý sloupec samostatně. V budoucím příspěvku prozkoumám, zda je tento scénář paralelní pro sloupce LOB.