ALTER TABLE ... ALTER COLUMN
kommandoen er meget kraftfuld. Du kan bruge den til at ændre en kolonnes datatype, længde, præcision, skala, nullbarhed, sortering ... og mange andre ting udover.
Det er bestemt mere bekvemt end alternativet:Oprettelse af en ny tabel og migrering af data, hver gang en ændring er nødvendig. Ikke desto mindre er der kun så meget, der kan gøres for at skjule den underliggende kompleksitet. Sammen med et stort antal restriktioner på, hvad der overhovedet er muligt med denne kommando, er der altid spørgsmålet om ydeevne.
I sidste ende gemmes tabeller som en sekvens af bytes med nogle metadata andre steder i systemet for at beskrive, hvad hver af disse bytes betyder, og hvordan de relaterer til hver af tabellens forskellige kolonner. Når vi beder SQL Server om at ændre nogle aspekter af en kolonnes definition, skal den kontrollere, at de eksisterende data er kompatible med den nye definition. Det skal også afgøre, om det aktuelle fysiske layout skal ændres.
Afhængigt af ændringstypen og konfigurationen af databasen vises en ALTER COLUMN
kommandoen skal udføre en af følgende handlinger:
- Skift kun metadata i systemtabeller.
- Tjek alle eksisterende data for kompatibilitet, og skift derefter metadata.
- Omskriv nogle af eller alle de lagrede data, så de matcher den nye definition.
Mulighed 1 repræsenterer det ideelle tilfælde ud fra et præstationssynspunkt. Det kræver kun få ændringer af systemtabeller og en minimal mængde logning. Operationen vil stadig kræve en restriktiv skemaændring Sch-M
lås, men selve metadataændringerne fuldføres meget hurtigt, uanset tabellens størrelse.
Ændringer kun for metadata
Der er en række særlige tilfælde at være opmærksom på, men som en generel oversigt kræver følgende handlinger kun ændringer af metadata:
- Gå fra
NOT NULL
tilNULL
for samme datatype. - Forøgelse af den maksimale størrelse af en
varchar
,nvarchar
, ellervarbinary
kolonne (undtagen tilmax
).
Forbedringer i SQL Server 2016
Emnet for dette indlæg er de yderligere ændringer, der er aktiveret for kun metadata fra SQL Server 2016 og frem . Ingen ændringer i syntaks er nødvendige, og ingen konfigurationsindstillinger skal ændres. Du får disse udokumenterede forbedringer gratis.
De nye funktioner er målrettet mod en undergruppe af fast længde datatyper. De nye egenskaber gælder for række-butikstabeller under følgende omstændigheder:
- Kompression skal være aktiveret:
- På alle indekser og partitioner , inklusive basisbunken eller klynget indeks.
- Enten
ROW
ellerPAGE
komprimering. - Indekser og partitioner kan bruge en blanding af disse kompressionsniveauer. Det vigtige er, at der ikke er nogen ukomprimerede indekser eller partitioner.
- Ændring fra
NULL
tilNOT NULL
er ikke tilladt . - Følgende heltalstype ændres er understøttet:
smallint
tilinteger
ellerbigint
.integer
tilbigint
.smallmoney
tilmoney
(bruger heltalsrepræsentation internt).
- Følgende ændringer af streng og binær type er understøttet:
char(n)
tilchar(m)
ellervarchar(m)
nchar(n)
tilnchar(m)
ellernvarchar(m)
binary(n)
tilbinary(m)
ellervarbinary(m)
- Alt ovenstående kun for
n < m
ogm != max
- Sorteringsændringer er ikke tilladt
Disse ændringer kan kun være metadata, fordi det underliggende binære datalayout ikke ændres, når Kolonnebeskrivelse rækkeformat bruges (derfor behovet for komprimering). Uden komprimering bruger rækkelager den originale FixedVar repræsentation, som ikke kan rumme disse datatypeændringer med fast længde uden at omskrive det fysiske layout.
Du bemærker måske, at tinyint
er udeladt fra listen over heltaltyper. Dette skyldes, at det er usigneret, mens de andre heltalstyper alle er signerede, så en ændring, der kun er metadata, er ikke mulig. For eksempel kan en værdi på 255 passe ind i én byte for tinyint
, men kræver to bytes i et hvilket som helst af de signerede formater. De signerede formater kan holde -128 til +127 i én byte, når de er komprimerede.
Heltalseksempel
En meget praktisk anvendelse af denne forbedring er at ændre datatypen for en kolonne med IDENTITY
ejendom.
Lad os sige, at vi har følgende heap-tabel ved hjælp af rækkekomprimering (sidekomprimering ville også fungere):
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);
Lad os tilføje 5 millioner rækker af data. Dette vil være nok til at gøre det indlysende (fra et præstationssynspunkt), om ændring af kolonnedatatypen kun er en metadata-operation eller ej:
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;
Dernæst vil vi gense IDENTITY
for at få det til at virke som om, vi næsten er ved at løbe tør for værdier, der passer ind i et integer
:
DBCC CHECKIDENT ( N'dbo.Test', RESEED, 2147483646 );
Vi kan tilføje en række mere med succes:
INSERT dbo.Test (some_value) VALUES (123456);
Men forsøger at tilføje en anden række:
INSERT dbo.Test (some_value) VALUES (7890);
Resulterer i en fejlmeddelelse:
Msg 8115, Level 16, State 1, Line 1Aritmetisk overløbsfejl ved konvertering af IDENTITY til datatype int.
Vi kan rette det ved at konvertere kolonnen til bigint
:
ALTER TABLE dbo.Test ALTER COLUMN id bigint NOT NULL;
Takket være forbedringerne i SQL Server 2016 ændrer denne kommando kun metadata , og afsluttes med det samme. Den forrige INSERT
sætning (den, der gav den aritmetiske overløbsfejl) fuldføres nu.
Denne nye evne løser ikke alle problemerne omkring ændring af typen af en kolonne med IDENTITY
ejendom. Vi bliver stadig nødt til at droppe og genskabe eventuelle indekser på kolonnen, genskabe eventuelle refererende fremmednøgler og så videre. Det er lidt uden for dette indlægs rammer (selvom Aaron Bertrand har skrevet om det før). At kunne ændre typen som en kun metadata-operation skader bestemt ikke. Med omhyggelig planlægning kan de øvrige nødvendige trin gøres så effektive som muligt, for eksempel ved at bruge minimalt logget eller ONLINE
operationer.
Vær forsigtig med syntaks
Sørg for at altid angiv NULL
eller NOT NULL
ved ændring af datatyper med ALTER COLUMN
. Lad os f.eks. sige, at vi også ønskede at ændre datatypen for some_value
kolonne i vores testtabel fra integer NOT NULL
til bigint NOT NULL
.
Når vi skriver kommandoen, udelader vi NULL
eller NOT NULL
kvalifikation:
ALTER TABLE dbo.Test ALTER COLUMN some_value bigint;
Denne kommando fuldføres med succes som en kun metadata-ændring, men fjerner også NOT NULL
begrænsning. Kolonnen er nu bigint NULL
, hvilket ikke er, hvad vi havde til hensigt. Denne adfærd er dokumenteret, men den er let at overse.
Vi forsøger muligvis at rette vores fejl med:
ALTER TABLE dbo.Test ALTER COLUMN some_value bigint NOT NULL;
Dette er ikke en ændring kun med metadata. Vi har ikke lov til at ændre fra NULL
til NOT NULL
(se tilbage til den tidligere tabel, hvis du har brug for en genopfriskning af betingelserne). SQL Server bliver nødt til at kontrollere alle eksisterende værdier for at sikre, at der ikke er nuller til stede. Det vil derefter fysisk omskrive hver række af bordet. Ud over at være langsomme i sig selv genererer disse handlinger en hel del transaktionslogfiler, som kan have afsmittende effekter.
Som en sidebemærkning er den samme fejl ikke mulig for kolonner med IDENTITY
ejendom. Hvis vi skriver en ALTER COLUMN
sætning uden NULL
eller NOT NULL
i så fald antager motoren hjælpsomt, at vi mente NOT NULL
fordi egenskaben identitet ikke er tilladt på nullable kolonner. Det er stadig en god idé ikke at stole på denne adfærd.
Angiv altid NULL
eller NOT NULL
med ALTER COLUMN
.
Samling
Særlig forsigtighed er nødvendig, når du ændrer en strengkolonne, der har en sortering, der ikke matcher standarden for databasen.
Lad os f.eks. sige, at vi har en tabel med en sortering, der er afhængig af store og små bogstaver og accent (antag, at databasens standard er anderledes):
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);
Tilføj 5 millioner rækker 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;
Fordoble længden af strengkolonnen ved hjælp af følgende kommando:
ALTER TABLE dbo.Test2 ALTER COLUMN some_string char(16) NOT NULL;
Vi huskede at angive NOT NULL
, men glemte ikke-standardsorteringen. SQL Server antager, at vi havde til hensigt at ændre sortering til databasens standard (Latin1_General_CI_AS
til min testdatabase). Ændring af sortering forhindrer handlingen i kun at være metadata, og operationen kører derfor i flere minutter og genererer dynger af log.
Genskab tabellen og dataene ved hjælp af det forrige script, og prøv derefter ALTER COLUMN
kommandoen igen, men angiver den eksisterende ikke-standardsortering som en del af kommandoen:
ALTER TABLE dbo.Test2 ALTER COLUMN some_string char(16) COLLATE Latin1_General_100_CS_AS NOT NULL;
Ændringen fuldføres nu med det samme, som en handling, der kun er metadata. Som med NULL
og NOT NULL
syntaks, kan det betale sig at være eksplicit for at undgå ulykker. Dette er et godt råd generelt, ikke kun for ALTER COLUMN
.
Kompression
Vær opmærksom på, at komprimering skal specificeres eksplicit for hvert indeks og separat for basistabellen, hvis det er en heap. Dette er endnu et eksempel, hvor brug af forkortet syntaks eller genveje kan forhindre det ønskede resultat.
For eksempel angiver følgende tabel ikke eksplicit komprimering for hverken primærnøgle eller in-line indeksdefinition:
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
vil have et navn tildelt, standard til CLUSTERED
,og være PAGE
komprimeret. Det in-line indeks vil være NONCLUSTERED
og slet ikke komprimeret. Denne tabel vil ikke være aktiveret for nogen af de nye optimeringer, fordi ikke alle indekser og partitioner er komprimeret.
En meget bedre og mere eksplicit tabeldefinition ville være:
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) );
Denne tabel vil kvalificere sig til de nye optimeringer, fordi alle indekser og partitioner er komprimeret. Som tidligere nævnt er det fint at blande komprimeringstyper.
Der er en række måder at skrive denne CREATE TABLE
på udsagn på en eksplicit måde, så der er et element af personlig præference. Det vigtige takeaway-punkt er altid at være eksplicit om hvad du vil. Dette gælder for separat CREATE INDEX
også udsagn.
Udvidede begivenheder og sporingsflag
Der er en udvidet begivenhed specifikt for den nye metadata-kun ALTER COLUMN
operationer understøttet i SQL Server 2016 og frem.
Den udvidede hændelse er compressed_alter_column_is_md_only
i Debug kanal. Dens hændelsesfelter er object_id
, column_id
, og is_md_only
(sandt/falskt).
Denne hændelse angiver kun, om en handling kun er metadata på grund af de nye muligheder i SQL Server 2016. Kolonneændringer, der kun var metadata før 2016, vil vise is_md_only = false
selvom det stadig kun er metadata.
Andre udvidede hændelser, der er nyttige til sporing af ALTER COLUMN
operationer inkluderer metadata_ddl_alter_column
og alter_column_event
, begge i Analytisk kanal.
Skulle du have brug for at deaktivere de nye SQL Server 2016-funktioner, uanset årsagen, kan udokumenteret global (eller opstart) sporingsflag 3618 bruges. Dette sporingsflag er ikke effektivt, når det bruges på sessionsniveau. Der er ingen måde at angive et sporingsflag på forespørgselsniveau med en ALTER COLUMN
kommando.
Sidste tanker
At være i stand til at ændre nogle heltaldatatyper med fast længde med en ændring kun med metadata er en meget velkommen produktforbedring. Det kræver, at bordet allerede er fuldt komprimeret, men det bliver alligevel mere almindeligt. Dette gælder især, da komprimering var aktiveret i alle udgaver, der starter med SQL Server 2016 Service Pack 1.
Kolonner af strengtype med fast længde er sandsynligvis meget mindre almindelige. Noget af dette kan skyldes noget forældede overvejelser såsom pladsforbrug. Når de er komprimerede, gemmer strengsøjler med fast længde ikke efterstillede emner, hvilket gør dem lige så effektive som strengkolonner med variabel længde fra et opbevaringssynspunkt. Det kan være irriterende at trimme mellemrum til manipulation eller visning, men hvis data normalt fylder det meste af den maksimale længde, kan typer med fast længde have vigtige fordele, ikke mindst hvad angår hukommelsesbevillinger til ting som sortering og hashing.
Det er ikke alle gode nyheder med komprimering aktiveret. Jeg nævnte tidligere, at SQL Server nogle gange kan udføre en kun metadata-ændring efter at have kontrolleret, at alle eksisterende værdier konverteres med succes til den nye type. Dette er tilfældet, når du bruger ALTER COLUMN
for at ændre fra integer
til smallint
for eksempel. Desværre er disse operationer i øjeblikket ikke kun metadata for komprimerede objekter.
Anerkendelser
Særlig tak til Panagiotis Antonopoulos (Principal Software Engineer) og Mirek Sztajno (Senior Program Manager) fra SQL Server-produktteamet for deres assistance og vejledning under research og skrivning af denne artikel.
Ingen af detaljerne i dette arbejde skal betragtes som officiel Microsoft-dokumentation eller produkterklæringer.