sql >> Database teknologi >  >> RDS >> Sqlserver

Nye metadata-kun kolonneændringer i SQL Server 2016

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:

  1. Skift kun metadata i systemtabeller.
  2. Tjek alle eksisterende data for kompatibilitet, og skift derefter metadata.
  3. 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 til NULL for samme datatype.
  • Forøgelse af den maksimale størrelse af en varchar , nvarchar , eller varbinary kolonne (undtagen til max ).

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:
    • alle indekser og partitioner , inklusive basisbunken eller klynget indeks.
    • Enten ROW eller PAGE 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 til NOT NULL er ikke tilladt .
  • Følgende heltalstype ændres er understøttet:
    • smallint til integer eller bigint .
    • integer til bigint .
    • smallmoney til money (bruger heltalsrepræsentation internt).
  • Følgende ændringer af streng og binær type er understøttet:
    • char(n) til char(m) eller varchar(m)
    • nchar(n) til nchar(m) eller nvarchar(m)
    • binary(n) til binary(m) eller varbinary(m)
    • Alt ovenstående kun for n < m og m != 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 1
Aritmetisk 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.


  1. Sådan fungerer NCHAR()-funktionen i SQL Server (T-SQL)

  2. Sådan indsætter du mere end 1000 værdier i en Oracle IN-klausul

  3. mysqli eller PDO - hvad er fordele og ulemper?

  4. Introduktion til Oracle Mobile Cloud Service