sql >> Databasteknik >  >> RDS >> Sqlserver

Nya kolumnändringar endast för metadata i SQL Server 2016

ALTER TABLE ... ALTER COLUMN kommandot är mycket kraftfullt. Du kan använda den för att ändra en kolumns datatyp, längd, precision, skala, nollbarhet, sortering ... och många andra saker förutom.

Det är säkert bekvämare än alternativet:Skapa en ny tabell och migrera data varje gång en ändring är nödvändig. Ändå finns det bara så mycket som kan göras för att dölja den underliggande komplexiteten. Tillsammans med ett stort antal restriktioner för vad som ens är möjligt med detta kommando, finns det alltid frågan om prestanda.

I slutändan lagras tabeller som en sekvens av byte med viss metadata någon annanstans i systemet för att beskriva vad var och en av dessa byte betyder och hur de relaterar till var och en av tabellens olika kolumner. När vi ber SQL Server att ändra någon aspekt av en kolumns definition måste den kontrollera att befintliga data är kompatibla med den nya definitionen. Det måste också avgöra om den nuvarande fysiska layouten behöver ändras.

Beroende på typen av ändring och konfigurationen av databasen visas en ALTER COLUMN kommandot måste utföra en av följande åtgärder:

  1. Ändra metadata endast i systemtabeller.
  2. Kontrollera all befintlig data för kompatibilitet och ändra sedan metadata.
  3. Skriv om några eller alla lagrade data för att matcha den nya definitionen.

Alternativ 1 representerar det idealiska fallet ur prestationssynpunkt. Det kräver bara några få ändringar av systemtabellerna och en minimal mängd loggning. Operationen kommer fortfarande att kräva en restriktiv schemaändring Sch-M lås, men själva metadataändringarna kommer att slutföras mycket snabbt, oavsett storleken på tabellen.

Ändringar endast för metadata

Det finns ett antal specialfall att se upp för, men som en allmän sammanfattning kräver följande åtgärder endast ändringar av metadata:

  • Gå från NOT NULL till NULL för samma datatyp.
  • Öka den maximala storleken för en varchar , nvarchar , eller varbinary kolumn (förutom max ).

Förbättringar i SQL Server 2016

Ämnet för det här inlägget är de ytterligare ändringar som är aktiverade för enbart metadata från SQL Server 2016 och framåt . Inga ändringar av syntax behövs och inga konfigurationsinställningar behöver ändras. Du får dessa odokumenterade förbättringar gratis.

De nya funktionerna är inriktade på en delmängd av fast längd datatyper. De nya förmågorna gäller för radbutikstabeller under följande omständigheter:

  • Komprimering måste vara aktiverad:
    • alla index och partitioner , inklusive bashögen eller klustrade index.
    • Antingen ROW eller PAGE komprimering.
    • Index och partitioner kan använda en blandning av dessa kompressionsnivåer. Det viktiga är att det inte finns några okomprimerade index eller partitioner.
  • Ändras från NULL till NOT NULL är inte tillåtet .
  • Följande heltalstyp ändras stöds:
    • smallint till integer eller bigint .
    • integer till bigint .
    • smallmoney till money (använder heltalsrepresentation internt).
  • Följande ändringar av sträng och binär typ stöds:
    • char(n) till char(m) eller varchar(m)
    • nchar(n) till nchar(m) eller nvarchar(m)
    • binary(n) till binary(m) eller varbinary(m)
    • Allt ovanstående endast för n < m och m != max
    • Sorteringsändringar är inte tillåtna

Dessa ändringar kan endast vara metadata eftersom den underliggande binära datalayouten inte ändras när kolumnbeskrivning radformat används (därav behovet av komprimering). Utan komprimering använder radlagring den ursprungliga FixedVar representation, som inte kan hantera dessa datatypsändringar med fast längd utan att skriva om den fysiska layouten.

Du kanske märker att tinyint utelämnas från listan med heltalstyper. Detta beror på att den är osignerad, medan de andra heltalstyperna alla är signerade, så en ändring av enbart metadata är inte möjlig. Till exempel kan ett värde på 255 rymmas i en byte för tinyint , men kräver två byte i något av de signerade formaten. De signerade formaten kan hålla -128 till +127 i en byte när de komprimeras.

Heltalsexempel

En mycket praktisk tillämpning av denna förbättring är att ändra datatypen för en kolumn med IDENTITY egendom.

Säg att vi har följande heaptabell med radkomprimering (sidkomprimering skulle också fungera):

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

Låt oss lägga till 5 miljoner rader med data. Detta kommer att räcka för att göra det uppenbart (ur prestandasynpunkt) huruvida att ändra kolumndatatypen är en operation av endast metadata eller inte:

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ärefter kommer vi att se om IDENTITY för att få det att verka som om vi nästan är på väg att ta slut på värden som passar i ett integer :

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

Vi kan lägga till ytterligare en rad framgångsrikt:

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

Men försöker lägga till en annan rad:

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

Resultatet blir ett felmeddelande:

Msg 8115, Level 16, State 1, Line 1
Aritmetiskt spillfel vid konvertering av IDENTITY till datatyp int.

Vi kan fixa det genom att konvertera kolumnen till bigint :

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

Tack vare förbättringarna i SQL Server 2016 ändrar detta kommando endast metadata , och slutförs omedelbart. Den föregående INSERT satsen (den som orsakade det aritmetiska spillfelet) slutförs nu framgångsrikt.

Den här nya förmågan löser inte alla problem kring att ändra kolumnstyp med IDENTITY fast egendom. Vi kommer fortfarande att behöva släppa och återskapa alla index på kolumnen, återskapa eventuella refererande främmande nycklar och så vidare. Det är lite utanför ramen för detta inlägg (även om Aaron Bertrand har skrivit om det tidigare). Att kunna ändra typen som enbart metadata-operation skadar verkligen inte. Med noggrann planering kan de andra stegen som krävs göras så effektiva som möjligt, till exempel genom att använda minimalt loggade eller ONLINE operationer.

Var försiktig med syntax

Se till att alltid ange NULL eller NOT NULL när du ändrar datatyper med ALTER COLUMN . Säg till exempel att vi också ville ändra datatypen för some_value kolumn i vår testtabell från integer NOT NULL till bigint NOT NULL .

När vi skriver kommandot utelämnar vi NULL eller NOT NULL kval:

ALTER TABLE dbo.Test
ALTER COLUMN some_value bigint;

Det här kommandot slutförs framgångsrikt som en ändring av endast metadata, men tar också bort NOT NULL begränsning. Kolumnen är nu bigint NULL , vilket inte var vad vi hade tänkt oss. Detta beteende är dokumenterat, men det är lätt att förbise.

Vi kan försöka åtgärda vårt misstag med:

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

Detta är inte en ändring av endast metadata. Vi får inte ändra från NULL till NOT NULL (se tillbaka till den tidigare tabellen om du behöver en uppfräschning om villkoren). SQL Server måste kontrollera alla befintliga värden för att säkerställa att inga nollvärden finns. Den kommer sedan fysiskt att skriva om varje rad av bordet. Förutom att de är långsamma i sig, genererar dessa åtgärder en hel del transaktionsloggar, vilket kan få utslagseffekter.

Som en sidoanteckning är samma misstag inte möjligt för kolumner med IDENTITY fast egendom. Om vi ​​skriver en ALTER COLUMN uttalande utan NULL eller NOT NULL i så fall antar motorn att vi menade NOT NULL eftersom identitetsegenskapen inte är tillåten på nullbara kolumner. Det är fortfarande en bra idé att inte lita på detta beteende.

Ange alltid NULL eller NOT NULL med ALTER COLUMN .

Sortering

Särskild försiktighet krävs när du ändrar en strängkolumn som har en sortering som inte matchar standardinställningen för databasen.

Säg till exempel att vi har en tabell med en skiftläges- och accentkänslig sortering (antag att databasens standard är annorlunda):

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

Lägg till 5 miljoner rader med data:

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;

Dubbla längden på strängkolumnen med följande kommando:

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

Vi kom ihåg att ange NOT NULL , men glömde bort den icke-standardiserade sorteringen. SQL Server antar att vi tänkte ändra sortering till databasstandarden (Latin1_General_CI_AS för min testdatabas). Genom att ändra sortering förhindrar åtgärden att endast vara metadata, så åtgärden körs i flera minuter, vilket genererar högar av loggar.

Återskapa tabellen och data med det föregående skriptet och försök sedan ALTER COLUMN kommandot igen, men ange den befintliga icke-standardsorteringen som en del av kommandot:

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

Ändringen slutförs nu omedelbart, som enbart metadata. Som med NULL och NOT NULL syntax, lönar det sig att vara tydlig för att undvika olyckor. Detta är ett bra råd i allmänhet, inte bara för ALTER COLUMN .

Kompression

Var medveten om att komprimering måste anges uttryckligen för varje index, och separat för bastabellen om det är en hög. Detta är ytterligare ett exempel där användning av förkortad syntax eller genvägar kan förhindra det önskade resultatet.

Följande tabell anger till exempel inte explicit komprimering för varken primärnyckeln eller in-line indexdefinition:

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 kommer att ha ett namn tilldelat, standard till CLUSTERED ,och vara PAGE komprimerad. In-line-indexet kommer att vara NONCLUSTERED och inte alls komprimerad. Den här tabellen kommer inte att aktiveras för någon av de nya optimeringarna eftersom inte alla index och partitioner är komprimerade.

En mycket bättre och mer explicit tabelldefinition skulle vara:

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

Den här tabellen kommer att kvalificera sig för de nya optimeringarna eftersom alla index och partitioner är komprimerade. Som nämnts tidigare går det bra att blanda komprimeringstyper.

Det finns en mängd olika sätt att skriva denna CREATE TABLE uttalande på ett explicit sätt, så det finns ett inslag av personlig preferens. Den viktiga punkten är att alltid vara tydlig om vad du vill. Detta gäller separat CREATE INDEX uttalanden också.

Utökade händelser och spårningsflagga

Det finns en utökad händelse specifikt för den nya ALTER COLUMN endast för metadata operationer som stöds i SQL Server 2016 och framåt.

Den utökade händelsen är compressed_alter_column_is_md_only i Felsökning kanal. Dess händelsefält är object_id , column_id och is_md_only (sant/falskt).

Den här händelsen indikerar bara om en operation är enbart metadata på grund av de nya funktionerna i SQL Server 2016. Kolumnändringar som endast var metadata före 2016 kommer att visa is_md_only = false trots att det fortfarande bara är metadata.

Andra utökade händelser som är användbara för att spåra ALTER COLUMN operationer inkluderar metadata_ddl_alter_column och alter_column_event , båda i Analytisk kanal.

Om du behöver inaktivera de nya SQL Server 2016-funktionerna av någon anledning, odokumenterad global (eller start-) spårningsflagga 3618 kan användas. Denna spårningsflagga är inte effektiv när den används på sessionsnivå. Det finns inget sätt att ange en spårningsflagga på frågenivå med en ALTER COLUMN kommando.

Slutliga tankar

Att kunna ändra vissa heltalsdatatyper med fast längd med en ändring av enbart metadata är en mycket välkommen produktförbättring. Det kräver visserligen att bordet redan är helt komprimerat, men det börjar bli mer vanligt ändå. Detta gäller särskilt eftersom komprimering var aktiverad i alla utgåvor från och med SQL Server 2016 Service Pack 1.

Kolumner av strängtyp med fast längd är förmodligen mycket mindre vanliga. En del av detta kan bero på något inaktuella överväganden som utrymmesanvändning. När de är komprimerade lagrar inte strängkolumner med fast längd efterföljande ämnen, vilket gör dem lika effektiva som strängkolumner med variabel längd ur lagringssynpunkt. Det kan vara irriterande att trimma utrymmen för manipulering eller visning, men om data vanligtvis upptar större delen av maxlängden kan typer av fast längd ha viktiga fördelar, inte minst vad gäller minnesanslag för saker som sortering och hash.

Det är inte alla goda nyheter med komprimering aktiverad. Jag nämnde tidigare att SQL Server ibland kan utföra en ändring av enbart metadata efter att ha kontrollerat att alla befintliga värden kommer att konverteras framgångsrikt till den nya typen. Detta är fallet när du använder ALTER COLUMN för att ändra från integer till smallint till exempel. Tyvärr är dessa operationer för närvarande inte enbart metadata för komprimerade objekt.

Bekräftelser

Särskilt tack till Panagiotis Antonopoulos (Principal Software Engineer) och Mirek Sztajno (Senior Program Manager) från SQL Server-produktteamet för deras hjälp och vägledning under forskningen och skrivningen av denna artikel.

Ingen av detaljerna i detta arbete ska betraktas som officiell Microsoft-dokumentation eller produktutlåtanden.


  1. Hämta den senaste raden för givet ID

  2. Säkerhetsstrategier inom datamodellering. Del 3

  3. TRANSLATE(… ANVÄNDER) Funktion i Oracle

  4. Villkorlig aggregeringsprestanda