Die ALTER TABLE ... ALTER COLUMN
Befehl ist sehr mächtig. Sie können es verwenden, um den Datentyp, die Länge, die Genauigkeit, die Skalierung, die Nullzulässigkeit, die Sortierung usw. einer Spalte zu ändern.
Es ist sicherlich bequemer als die Alternative:Bei jeder notwendigen Änderung eine neue Tabelle anlegen und die Daten migrieren. Dennoch kann nur begrenzt viel getan werden, um die zugrunde liegende Komplexität zu verbergen. Neben vielen Einschränkungen, was mit diesem Befehl überhaupt möglich ist, stellt sich immer die Frage nach der Performance.
Letztendlich werden Tabellen als eine Folge von Bytes mit einigen Metadaten an anderer Stelle im System gespeichert, um zu beschreiben, was jedes dieser Bytes bedeutet und wie sie sich auf die verschiedenen Spalten der Tabelle beziehen. Wenn wir SQL Server auffordern, einen Aspekt der Definition einer Spalte zu ändern, muss überprüft werden, ob die vorhandenen Daten mit der neuen Definition kompatibel sind. Außerdem muss festgestellt werden, ob das aktuelle physische Layout geändert werden muss.
Abhängig von der Art der Änderung und der Konfiguration der Datenbank, ein ALTER COLUMN
Befehl muss eine der folgenden Aktionen ausführen:
- Metadaten nur in Systemtabellen ändern.
- Überprüfen Sie alle vorhandenen Daten auf Kompatibilität und ändern Sie dann die Metadaten.
- Schreiben Sie einige oder alle gespeicherten Daten um, damit sie der neuen Definition entsprechen.
Option 1 stellt aus Performance-Sicht den Idealfall dar. Es erfordert nur wenige Änderungen an Systemtabellen und eine minimale Protokollierung. Die Operation erfordert weiterhin eine restriktive Schemaänderung Sch-M
lock, aber die Metadatenänderungen selbst werden sehr schnell abgeschlossen, unabhängig von der Größe der Tabelle.
Nur-Metadaten-Änderungen
Es gibt eine Reihe von Sonderfällen, auf die Sie achten müssen, aber als allgemeine Zusammenfassung erfordern die folgenden Aktionen nur Änderungen an Metadaten:
- Ausgehend von
NOT NULL
aufNULL
für denselben Datentyp. - Erhöhung der maximalen Größe eines
varchar
,nvarchar
, odervarbinary
Spalte (außer bismax
).
Verbesserungen in SQL Server 2016
Das Thema dieses Beitrags sind die zusätzlichen Änderungen, die ab SQL Server 2016 nur für Metadaten aktiviert sind . Es sind keine Änderungen an der Syntax erforderlich, und es müssen keine Konfigurationseinstellungen geändert werden. Sie erhalten diese undokumentierten Verbesserungen kostenlos.
Die neuen Funktionen zielen auf eine Teilmenge der Festlänge ab Datentypen. Die neuen Fähigkeiten gelten unter den folgenden Umständen für Zeilenspeichertabellen:
- Komprimierung muss aktiviert sein:
- Auf allen Indizes und Partitionen , einschließlich des Basisheaps oder Clustered-Index.
- Entweder
ROW
oderPAGE
Komprimierung. - Indizes und Partitionen können eine Mischung verwenden dieser Kompressionsstufen. Wichtig ist, dass es keine unkomprimierten Indizes oder Partitionen gibt.
- Wechsel von
NULL
zuNOT NULL
ist nicht erlaubt . - Die folgenden ganzzahligen Änderungen werden unterstützt:
smallint
zuinteger
oderbigint
.integer
zubigint
.smallmoney
zumoney
(verwendet intern die Ganzzahldarstellung).
- Die folgenden String- und Binärtypen ändern sich werden unterstützt:
char(n)
zuchar(m)
odervarchar(m)
nchar(n)
zunchar(m)
odernvarchar(m)
binary(n)
zubinary(m)
odervarbinary(m)
- Alles Obige nur für
n < m
undm != max
- Sortierungsänderungen sind nicht erlaubt
Diese Änderungen können nur Metadaten sein, da sich das zugrunde liegende binäre Datenlayout nicht ändert, wenn Column Descriptor Zeilenformat verwendet (daher die Notwendigkeit der Komprimierung). Ohne Komprimierung verwendet der Zeilenspeicher die ursprüngliche FixedVar Darstellung, die diese Datentypänderungen mit fester Länge nicht aufnehmen kann, ohne das physische Layout neu zu schreiben.
Sie werden vielleicht bemerken, dass tinyint
wird aus der Liste der Integer-Typen weggelassen. Dies liegt daran, dass es unsigniert ist, während die anderen Integer-Typen alle signiert sind, sodass eine Änderung nur der Metadaten nicht möglich ist. Beispielsweise kann ein Wert von 255 in ein Byte für tinyint
passen , erfordert aber zwei Bytes in jedem der signierten Formate. Die vorzeichenbehafteten Formate können komprimiert -128 bis +127 in einem Byte enthalten.
Integer-Beispiel
Eine sehr praktische Anwendung dieser Verbesserung ist das Ändern des Datentyps einer Spalte mit dem IDENTITY
Eigentum.
Angenommen, wir haben die folgende Heap-Tabelle mit Zeilenkomprimierung (Seitenkomprimierung würde auch funktionieren):
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);
Lassen Sie uns 5 Millionen Datenzeilen hinzufügen. Dies reicht aus, um (vom Leistungsstandpunkt aus) deutlich zu machen, ob das Ändern des Spaltendatentyps eine reine Metadatenoperation ist oder nicht:
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;
Als nächstes werden wir die IDENTITY
neu setzen um den Anschein zu erwecken, dass uns fast die Werte ausgehen, die in eine integer
passen :
DBCC CHECKIDENT ( N'dbo.Test', RESEED, 2147483646 );
Wir können erfolgreich eine weitere Zeile hinzufügen:
INSERT dbo.Test (some_value) VALUES (123456);
Aber der Versuch, eine weitere Zeile hinzuzufügen:
INSERT dbo.Test (some_value) VALUES (7890);
Führt zu einer Fehlermeldung:
Msg 8115, Level 16, State 1, Line 1Arithmetischer Überlauffehler beim Konvertieren von IDENTITY in den Datentyp int.
Wir können das beheben, indem wir die Spalte in bigint
konvertieren :
ALTER TABLE dbo.Test ALTER COLUMN id bigint NOT NULL;
Dank der Verbesserungen in SQL Server 2016 ändert dieser Befehl nur Metadaten , und wird sofort abgeschlossen. Das vorherige INSERT
-Anweisung (diejenige, die den arithmetischen Überlauffehler ausgelöst hat) wird nun erfolgreich abgeschlossen.
Diese neue Fähigkeit löst nicht alle Probleme beim Ändern des Typs einer Spalte mit IDENTITY
Eigentum. Wir müssen weiterhin alle Indizes für die Spalte löschen und neu erstellen, alle referenzierenden Fremdschlüssel neu erstellen und so weiter. Das würde den Rahmen dieses Beitrags etwas sprengen (obwohl Aaron Bertrand schon einmal darüber geschrieben hat). Die Möglichkeit, den Typ als reine Metadatenoperation zu ändern, schadet sicherlich nicht. Bei sorgfältiger Planung können die anderen erforderlichen Schritte so effizient wie möglich gestaltet werden, beispielsweise durch minimal protokollierte oder ONLINE
Operationen.
Seien Sie vorsichtig mit der Syntax
Achten Sie darauf, immer Geben Sie NULL
an oder NOT NULL
beim Ändern von Datentypen mit ALTER COLUMN
. Angenommen, wir wollten auch den Datentyp von some_value
ändern Spalte in unserer Testtabelle von integer NOT NULL
zu bigint NOT NULL
.
Wenn wir den Befehl schreiben, lassen wir NULL
weg oder NOT NULL
Qualifizierer:
ALTER TABLE dbo.Test ALTER COLUMN some_value bigint;
Dieser Befehl wird erfolgreich als Nur-Metadaten-Änderung abgeschlossen, entfernt aber auch NOT NULL
Zwang. Die Spalte ist jetzt bigint NULL
, was nicht unsere Absicht war. Dieses Verhalten ist dokumentiert, aber leicht zu übersehen.
Wir könnten versuchen, unseren Fehler zu beheben mit:
ALTER TABLE dbo.Test ALTER COLUMN some_value bigint NOT NULL;
Das ist nicht eine reine Metadatenänderung. Wir dürfen nicht von NULL
wechseln zu NOT NULL
(Beziehen Sie sich auf die vorherige Tabelle, wenn Sie eine Auffrischung der Bedingungen benötigen). SQL Server muss alle vorhandenen Werte überprüfen, um sicherzustellen, dass keine Nullen vorhanden sind. Es wird dann physisch jede Zeile neu schreiben des Tisches. Diese Aktionen sind nicht nur an sich langsam, sondern erzeugen auch eine Menge Transaktionsprotokolle, die Folgewirkungen haben können.
Nebenbei bemerkt, derselbe Fehler ist für Spalten mit dem IDENTITY
nicht möglich Eigentum. Wenn wir ein ALTER COLUMN
schreiben Anweisung ohne NULL
oder NOT NULL
in diesem Fall geht die Engine hilfreicherweise davon aus, dass wir NOT NULL
gemeint haben da die Identitätseigenschaft für Nullable-Spalten nicht zulässig ist. Es ist dennoch eine gute Idee, sich nicht auf dieses Verhalten zu verlassen.
Geben Sie immer NULL
an oder NOT NULL
mit ALTER COLUMN
.
Sortierung
Besondere Vorsicht ist geboten, wenn eine Zeichenfolgenspalte geändert wird, deren Sortierung nicht mit der Standardeinstellung für die Datenbank übereinstimmt.
Nehmen wir zum Beispiel an, wir haben eine Tabelle mit einer Kollatierung, bei der zwischen Groß- und Kleinschreibung und Akzenten unterschieden wird (vorausgesetzt, der Datenbankstandard ist anders):
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);
Fügen Sie 5 Millionen Datenzeilen hinzu:
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;
Verdoppeln Sie die Länge der String-Spalte mit dem folgenden Befehl:
ALTER TABLE dbo.Test2 ALTER COLUMN some_string char(16) NOT NULL;
Wir haben daran gedacht, NOT NULL
anzugeben , aber die nicht standardmäßige Sortierung vergessen. SQL Server geht davon aus, dass wir die Sortierung auf den Datenbankstandard ändern wollten (Latin1_General_CI_AS
für meine Testdatenbank). Durch das Ändern der Sortierung wird verhindert, dass die Operation nur Metadaten enthält, und daher wird die Operation mehrere Minuten lang ausgeführt und erzeugt Unmengen von Protokollen.
Erstellen Sie die Tabelle und die Daten mit dem vorherigen Skript neu und versuchen Sie es dann mit ALTER COLUMN
erneut ausführen, aber die vorhandene nicht standardmäßige Sortierung als Teil des Befehls angeben:
ALTER TABLE dbo.Test2 ALTER COLUMN some_string char(16) COLLATE Latin1_General_100_CS_AS NOT NULL;
Die Änderung wird jetzt sofort als reiner Metadatenvorgang abgeschlossen. Wie beim NULL
und NOT NULL
Syntax, lohnt es sich, explizit zu sein, um Unfälle zu vermeiden. Dies ist allgemein ein guter Rat, nicht nur für ALTER COLUMN
.
Komprimierung
Bitte beachten Sie, dass die Komprimierung explizit für jeden Index und separat für die Basistabelle angegeben werden muss, wenn es sich um einen Heap handelt. Dies ist ein weiteres Beispiel, bei dem die Verwendung von abgekürzter Syntax oder Abkürzungen das gewünschte Ergebnis verhindern kann.
Beispielsweise gibt die folgende Tabelle keine explizite Komprimierung für den Primärschlüssel oder die Inline-Indexdefinition an:
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);
Der PRIMARY KEY
wird ein Name zugewiesen, standardmäßig CLUSTERED
, und sei PAGE
komprimiert. Der Inline-Index ist NONCLUSTERED
und überhaupt nicht komprimiert. Diese Tabelle wird für keine der neuen Optimierungen aktiviert, da nicht alle Indizes und Partitionen komprimiert sind.
Eine viel bessere und explizitere Tabellendefinition wä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) );
Diese Tabelle eignet sich für die neuen Optimierungen, da alle Indizes und Partitionen komprimiert sind. Wie bereits erwähnt, ist das Mischen von Komprimierungstypen in Ordnung.
Es gibt verschiedene Möglichkeiten, diese CREATE TABLE
zu schreiben ausdrückliche Aussage, also gibt es ein Element persönlicher Vorlieben. Der wichtige Punkt zum Mitnehmen ist, immer explizit zu sein über das, was Sie wollen. Dies gilt für separates CREATE INDEX
auch Aussagen.
Erweiterte Ereignisse und Trace-Flag
Es gibt ein erweitertes Ereignis speziell für das neue Nur-Metadaten-ALTER COLUMN
Operationen, die ab SQL Server 2016 unterstützt werden.
Das erweiterte Ereignis ist compressed_alter_column_is_md_only
im Debug Kanal. Seine Ereignisfelder sind object_id
, column_id
, und is_md_only
(wahr/falsch).
Dieses Ereignis zeigt nur an, ob ein Vorgang aufgrund der neuen Fähigkeiten von SQL Server 2016 nur Metadaten ist. Spaltenänderungen, die vor 2016 nur Metadaten waren, zeigen is_md_only = false
obwohl es immer noch nur Metadaten sind.
Andere erweiterte Ereignisse, die zum Verfolgen von ALTER COLUMN
nützlich sind Operationen beinhalten metadata_ddl_alter_column
und alter_column_event
, beide in der Analyse Kanal.
Sollten Sie deaktivieren müssen die neuen SQL Server 2016-Funktionen aus irgendeinem Grund, das undokumentierte globale (oder Start-) Ablaufverfolgungsflag 3618 verwendet werden. Dieses Ablaufverfolgungsflag ist nicht wirksam, wenn es auf Sitzungsebene verwendet wird. Es gibt keine Möglichkeit, ein Trace-Flag auf Abfrageebene mit einem ALTER COLUMN
anzugeben Befehl.
Abschließende Gedanken
Die Möglichkeit, einige ganzzahlige Datentypen fester Länge mit einer reinen Metadatenänderung zu ändern, ist eine sehr willkommene Produktverbesserung. Es erfordert zwar, dass die Tabelle bereits vollständig komprimiert ist, aber das wird sowieso immer häufiger. Dies gilt insbesondere, da die Komprimierung in allen Editionen ab SQL Server 2016 Service Pack 1 aktiviert wurde.
Spalten vom Typ Zeichenfolge mit fester Länge sind wahrscheinlich viel seltener. Einiges davon kann auf etwas veraltete Überlegungen wie die Speicherplatznutzung zurückzuführen sein. Wenn sie komprimiert sind, speichern Zeichenfolgenspalten mit fester Länge keine nachgestellten Leerzeichen, wodurch sie vom Speicherstandpunkt aus genauso effizient sind wie Zeichenfolgenspalten mit variabler Länge. Es kann lästig sein, Leerzeichen zur Manipulation oder Anzeige zu kürzen, aber wenn die Daten normalerweise den größten Teil der maximalen Länge einnehmen, können Typen mit fester Länge wichtige Vorteile haben, nicht zuletzt in Bezug auf Speicherzuteilungen für Dinge wie Sortierung und Hashing.
Es sind nicht alle guten Nachrichten, wenn die Komprimierung aktiviert ist. Ich habe bereits erwähnt, dass SQL Server manchmal eine reine Metadatenänderung durchführen kann, nachdem überprüft wurde, ob alle vorhandenen Werte erfolgreich in den neuen Typ konvertiert werden. Dies ist der Fall, wenn ALTER COLUMN
verwendet wird um von integer
zu wechseln zu smallint
zum Beispiel. Leider sind diese Vorgänge derzeit nicht nur Metadaten für komprimierte Objekte.
Danksagungen
Besonderer Dank geht an Panagiotis Antonopoulos (Principal Software Engineer) und Mirek Sztajno (Senior Program Manager) vom SQL Server-Produktteam für ihre Unterstützung und Anleitung während der Recherche und dem Schreiben dieses Artikels.
Keine der in dieser Arbeit enthaltenen Details sollte als offizielle Microsoft-Dokumentation oder Produktaussage betrachtet werden.