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

Nové změny sloupců pouze metadata v SQL Server 2016

ALTER TABLE ... ALTER COLUMN příkaz je velmi silný. Můžete jej použít ke změně datového typu sloupce, délky, přesnosti, měřítka, možnosti null, řazení...a mnoha dalších věcí.

Je to určitě pohodlnější než alternativa:Vytvoření nové tabulky a migrace dat pokaždé, když je nutná změna. Nicméně, existuje jen tolik, co lze udělat pro skrytí základní složitosti. Spolu s velkým množstvím omezení toho, co je s tímto příkazem vůbec možné, je tu vždy otázka výkonu.

Nakonec jsou tabulky uloženy jako sekvence bajtů s některými metadaty jinde v systému, aby bylo popsáno, co každý z těchto bajtů znamená a jak souvisí s každým z různých sloupců tabulky. Když SQL Server požádáme o změnu některého aspektu definice sloupce, potřebuje zkontrolovat, zda jsou stávající data kompatibilní s novou definicí. Musí také určit, zda je třeba změnit aktuální fyzické rozvržení.

V závislosti na typu změny a konfiguraci databáze ALTER COLUMN příkaz bude muset provést jednu z následujících akcí:

  1. Změňte metadata pouze v systémových tabulkách.
  2. Zkontrolujte kompatibilitu všech stávajících dat a poté změňte metadata.
  3. Přepište některá nebo všechna uložená data tak, aby odpovídala nové definici.

Možnost 1 představuje ideální případ z hlediska výkonu. Vyžaduje pouze několik změn v systémových tabulkách a minimální množství protokolování. Operace bude stále vyžadovat restriktivní úpravu schématu Sch-M lock, ale samotné změny metadat se dokončí velmi rychle, bez ohledu na velikost tabulky.

Změny pouze metadat

Existuje řada zvláštních případů, na které je třeba dávat pozor, ale jako obecné shrnutí lze uvést, že následující akce vyžadují pouze změny metadat:

  • Přechod z NOT NULL na NULL pro stejný typ dat.
  • Zvětšení maximální velikosti varchar , nvarchar , nebo varbinary sloupec (kromě max ).

Vylepšení v SQL Server 2016

Předmětem tohoto příspěvku jsou další změny, které jsou povoleny pouze pro metadata od SQL Server 2016 . Nejsou potřeba žádné změny syntaxe a není třeba upravovat žádná konfigurační nastavení. Tato nedokumentovaná vylepšení získáte zdarma.

Nové možnosti se zaměřují na podmnožinu pevné délky typy dat. Nové schopnosti se vztahují na tabulky s úložištěm řádků za následujících okolností:

  • Komprese musí být povolena:
    • Na všech indexech a oddílech , včetně základní haldy nebo seskupeného indexu.
    • Buď ROW nebo PAGE komprese.
    • Indexy a oddíly mohou používat směs těchto úrovní komprese. Důležité je, že neexistují žádné nekomprimované indexy ani oddíly.
  • Změna z NULL na NOT NULL není povoleno .
  • Následující změní se typ celého čísla jsou podporovány:
    • smallint na integer nebo bigint .
    • integer na bigint .
    • smallmoney na money (interně používá reprezentaci celého čísla).
  • Následující změny řetězců a binárního typu jsou podporovány:
    • char(n) na char(m) nebo varchar(m)
    • nchar(n) na nchar(m) nebo nvarchar(m)
    • binary(n) na binary(m) nebo varbinary(m)
    • Vše výše uvedené pouze pro n < m a m != max
    • Změny řazení nejsou povoleny

Tyto změny mohou být pouze metadata, protože základní rozložení binárních dat se nezmění, když Deskriptor sloupce používá se řádkový formát (proto je potřeba komprese). Bez komprese používá úložiště řádků původní FixedVar reprezentace, která se nemůže přizpůsobit těmto změnám datového typu s pevnou délkou bez přepsání fyzického rozvržení.

Můžete si všimnout, že tinyint je ze seznamu celočíselných typů vynechán. Je to proto, že je bez znaménka, zatímco ostatní celočíselné typy jsou všechny podepsané, takže změna pouze metadat není možná. Například hodnota 255 se vejde do jednoho bajtu pro tinyint , ale vyžaduje dva bajty v libovolném z podepsaných formátů. Podepsané formáty mohou po kompresi obsahovat -128 až +127 v jednom byte.

Příklad celého čísla

Jednou z velmi praktických aplikací tohoto vylepšení je změna datového typu sloupce pomocí IDENTITY vlastnost.

Řekněme, že máme následující tabulku haldy používající kompresi řádků (komprese stránky by také fungovala):

DROP TABLE IF EXISTS dbo.Test;
GO
CREATE TABLE dbo.Test
(
    id integer IDENTITY NOT NULL,
    some_value integer NOT NULL
)
WITH (DATA_COMPRESSION = ROW);

Přidejme 5 milionů řádků dat. To bude stačit k tomu, aby bylo zřejmé (z hlediska výkonu), zda je změna datového typu sloupce operací pouze s metadaty či nikoli:

WITH Numbers AS
(
    SELECT 
        n = ROW_NUMBER() OVER (ORDER BY @@SPID) 
    FROM sys.all_columns AS AC1
    CROSS JOIN sys.all_columns AS AC2
    ORDER BY n
    OFFSET 0 ROWS
    FETCH FIRST 5 * 1000 * 1000 ROWS ONLY
)
INSERT dbo.Test
    WITH (TABLOCKX)
(
    some_value
)
SELECT
    N.n
FROM Numbers AS N;

Dále znovu nasadíme IDENTITY aby to vypadalo, že jsme téměř v okamžiku, kdy nám dojdou hodnoty, které se vejdou do integer :

DBCC CHECKIDENT
(
    N'dbo.Test',
    RESEED,
    2147483646
);

Můžeme úspěšně přidat další řádek:

INSERT dbo.Test
    (some_value)
VALUES
    (123456);

Ale pokouším se přidat další řádek:

INSERT dbo.Test
    (some_value)
VALUES
    (7890);

Výsledkem je chybová zpráva:

Msg 8115, Level 16, State 1, Line 1
Aritmetická chyba přetečení při převodu IDENTITY na datový typ int.

Můžeme to opravit převedením sloupce na bigint :

ALTER TABLE dbo.Test
ALTER COLUMN id bigint NOT NULL;

Díky vylepšení v SQL Server 2016 tento příkaz mění pouze metadata a je okamžitě dokončena. Předchozí INSERT příkaz (ten, který vyvolal chybu aritmetického přetečení) je nyní úspěšně dokončen.

Tato nová schopnost neřeší všechny problémy kolem změny typu sloupce pomocí IDENTITY vlastnictví. Stále budeme muset vypustit a znovu vytvořit všechny indexy ve sloupci, znovu vytvořit všechny odkazující cizí klíče a tak dále. To je trochu mimo rámec tohoto příspěvku (ačkoli o tom Aaron Bertrand psal již dříve). Možnost změnit typ jako operace pouze s metadaty rozhodně neuškodí. Při pečlivém plánování mohou být další požadované kroky co nejúčinnější, například pomocí minimálně protokolovaného nebo ONLINE operace.

Buďte opatrní se syntaxí

Ujistěte se, že vždy zadejte NULL nebo NOT NULL při změně datových typů pomocí ALTER COLUMN . Řekněme například, že jsme chtěli také změnit datový typ some_value sloupec v naší testovací tabulce z integer NOT NULL na bigint NOT NULL .

Při psaní příkazu vynecháme NULL nebo NOT NULL kvalifikátor:

ALTER TABLE dbo.Test
ALTER COLUMN some_value bigint;

Tento příkaz se úspěšně dokončí jako změna pouze metadat, ale také odstraní NOT NULL omezení. Sloupec je nyní bigint NULL , což není to, co jsme zamýšleli. Toto chování je zdokumentováno, ale lze jej snadno přehlédnout.

Můžeme se pokusit naši chybu napravit pomocí:

ALTER TABLE dbo.Test
ALTER COLUMN some_value bigint NOT NULL;

Toto není změna pouze metadat. Nesmíme změnit z NULL na NOT NULL (Pokud si potřebujete zopakovat podmínky, podívejte se zpět na předchozí tabulku). SQL Server bude muset zkontrolovat všechny existující hodnoty, aby se ujistil, že nejsou přítomny žádné hodnoty null. Poté fyzicky přepíše každý řádek tabulky. Kromě toho, že jsou samy o sobě pomalé, tyto akce generují velké množství protokolu transakcí, což může mít vedlejší efekty.

Jako vedlejší poznámka, stejná chyba není možná u sloupců s IDENTITY vlastnictví. Pokud napíšeme ALTER COLUMN příkaz bez NULL nebo NOT NULL v takovém případě motor užitečně předpokládá, že jsme mysleli NOT NULL protože vlastnost identity není povolena ve sloupcích s možnou hodnotou Null. Stále je skvělý nápad nespoléhat na toto chování.

Vždy zadejte NULL nebo NOT NULL s ALTER COLUMN .

Řazení

Zvláštní opatrnosti je zapotřebí při změně sloupce řetězce, jehož řazení neodpovídá výchozímu nastavení pro databázi.

Řekněme například, že máme tabulku s řazením citlivým na velká a malá písmena (předpokládejme, že výchozí nastavení databáze je jiné):

DROP TABLE IF EXISTS dbo.Test2;
GO
CREATE TABLE dbo.Test2
(
    id integer IDENTITY NOT NULL,
    some_string char(8) COLLATE Latin1_General_100_CS_AS NOT NULL
)
WITH (DATA_COMPRESSION = ROW);

Přidejte 5 milionů řádků dat:

WITH Numbers AS
(
    SELECT 
        n = ROW_NUMBER() OVER (ORDER BY @@SPID) 
    FROM sys.all_columns AS AC1
    CROSS JOIN sys.all_columns AS AC2
    ORDER BY n
    OFFSET 0 ROWS
    FETCH FIRST 5 * 1000 * 1000 ROWS ONLY
)
INSERT dbo.Test2
    WITH (TABLOCKX)
(
    some_string
)
SELECT
    CONVERT(char(8), N.n) COLLATE Latin1_General_100_CS_AS
FROM Numbers AS N;

Zdvojnásobte délku sloupce řetězce pomocí následujícího příkazu:

ALTER TABLE dbo.Test2
ALTER COLUMN some_string char(16) NOT NULL;

Nezapomněli jsme zadat NOT NULL , ale zapomněli na nestandardní řazení. SQL Server předpokládá, že jsme chtěli změnit řazení na výchozí databázi (Latin1_General_CI_AS pro mou testovací databázi). Změna řazení zabrání tomu, aby operace byla pouze metadata, a tak operace běží několik minut a generuje hromady protokolů.

Znovu vytvořte tabulku a data pomocí předchozího skriptu a poté vyzkoušejte ALTER COLUMN příkaz znovu, ale specifikující existující nevýchozí řazení jako součást příkazu:

ALTER TABLE dbo.Test2
ALTER COLUMN some_string 
    char(16) COLLATE Latin1_General_100_CS_AS NOT NULL;

Změna se nyní dokončí okamžitě jako operace pouze s metadaty. Stejně jako u NULL a NOT NULL syntaxe, vyplatí se být explicitní, aby se předešlo nehodám. Toto je dobrá rada obecně, nejen pro ALTER COLUMN .

Komprese

Uvědomte si prosím, že komprese musí být výslovně specifikována pro každý index a zvlášť pro základní tabulku, pokud se jedná o haldu. Toto je další příklad, kdy použití zkrácené syntaxe nebo zkratek může zabránit požadovanému výsledku.

Například následující tabulka neurčuje explicitní kompresi pro primární klíč ani definici indexu v řádku:

CREATE TABLE dbo.Test
(
    id integer IDENTITY NOT NULL PRIMARY KEY,
    some_value integer NOT NULL
        INDEX [IX dbo.Test some_value]
)
WITH (DATA_COMPRESSION = PAGE);

PRIMARY KEY bude mít přiřazen název, výchozí je CLUSTERED a být PAGE stlačený. Vložený index bude NONCLUSTERED a vůbec ne stlačený. Tato tabulka nebude povolena pro žádnou z nových optimalizací, protože ne všechny indexy a oddíly jsou komprimovány.

Mnohem lepší a explicitnější definice tabulky by byla:

CREATE TABLE dbo.Test
(
    id integer IDENTITY NOT NULL
        CONSTRAINT [PK dbo.Test id]
        PRIMARY KEY CLUSTERED
        WITH (DATA_COMPRESSION = PAGE),
    some_value integer NOT NULL
        INDEX [IX dbo.Test some_value]
        NONCLUSTERED
        WITH (DATA_COMPRESSION = ROW)        
);

Tato tabulka bude způsobilá pro nové optimalizace, protože všechny indexy a oddíly jsou komprimované. Jak bylo uvedeno dříve, míchání typů komprese je v pořádku.

Tuto CREATE TABLE lze napsat různými způsoby prohlášení explicitním způsobem, takže je zde prvek osobní preference. Důležitým bodem je vždy vyjadřovat se jasně o tom, co chcete. To platí pro samostatný CREATE INDEX také prohlášení.

Rozšířené události a příznak trasování

Existuje rozšířená událost speciálně pro nový ALTER COLUMN pouze pro metadata operace podporované v SQL Server 2016 a novějších.

Rozšířená událost je compressed_alter_column_is_md_only v Ladění kanál. Jeho pole událostí jsou object_id , column_id a is_md_only (pravda/nepravda).

Tato událost pouze označuje, zda je operace pouze s metadaty kvůli novým schopnostem SQL Server 2016. Změny sloupců, které byly před rokem 2016 pouze metadaty, zobrazí is_md_only = false přestože stále obsahuje pouze metadata.

Další rozšířené události užitečné pro sledování ALTER COLUMN operace zahrnují metadata_ddl_alter_column a alter_column_event , obojí v Analytickém kanál.

Pokud potřebujete deaktivovat nové funkce SQL Server 2016 z jakéhokoli důvodu, lze použít nezdokumentovaný globální (nebo spouštěcí) příznak trasování 3618. Tento příznak trasování není účinný při použití na úrovni relace. Neexistuje žádný způsob, jak určit příznak trasování na úrovni dotazu pomocí ALTER COLUMN příkaz.

Závěrečné myšlenky

Možnost změnit některé celočíselné datové typy s pevnou délkou změnou pouze metadat je velmi vítaným vylepšením produktu. Vyžaduje to, aby byla tabulka již plně zkomprimovaná, ale to se stejně stává běžnější věcí. To platí zejména proto, že komprese byla povolena ve všech edicích počínaje SQL Server 2016 Service Pack 1.

Sloupce typu řetězec s pevnou délkou jsou pravděpodobně mnohem méně běžné. Některé z toho mohou být způsobeny poněkud zastaralými úvahami, jako je využití prostoru. Když jsou zkomprimovány, sloupce řetězce s pevnou délkou neukládají koncové mezery, takže jsou z hlediska úložiště stejně efektivní jako sloupce s proměnnou délkou. Může být nepříjemné ořezávat prostory pro manipulaci nebo zobrazení, ale pokud data obvykle zabírají většinu maximální délky, mohou mít typy s pevnou délkou důležité výhody, v neposlední řadě pokud jde o přidělení paměti pro věci, jako je třídění a hašování.

S povolenou kompresí to nejsou všechny dobré zprávy. Již dříve jsem zmínil, že SQL Server může někdy provést změnu pouze metadat po kontrole, že všechny existující hodnoty se úspěšně převedou na nový typ. To je případ použití ALTER COLUMN změnit z integer na smallint například. Bohužel tyto operace nejsou v současné době pouze metadaty pro komprimované objekty.

Poděkování

Zvláštní poděkování patří Panagiotis Antonopoulos (hlavní softwarový inženýr) a Mirek Sztajno (Senior Program Manager) z produktového týmu SQL Server za jejich pomoc a vedení během výzkumu a psaní tohoto článku.

Žádný z podrobností uvedených v této práci by neměl být považován za oficiální dokumentaci společnosti Microsoft nebo prohlášení o produktu.


  1. 3 způsoby formátování čísla na 2 desetinná místa v Oracle

  2. ORA-12505, TNS:listener aktuálně nezná SID uvedené v connect des

  3. Export MySQL do výstupního souboru:CSV escaping chars

  4. Výpočet rozdílu mezi dvěma časovými razítky v Oracle v milisekundách