Effiziente Möglichkeit, VARCHAR- in NVARCHAR-Felder in einer großen Tabelle in SQL Server 2008 zu ändern?

7

Ich bin mir bewusst, dass beim Hinzufügen neuer Felder zu großen Tabellen empfohlen wird, diese am Ende der Felder und nicht irgendwo in der Mitte hinzuzufügen, und sich zu fragen, ob so etwas beim Ändern von Feldtypen zutrifft.

Ich habe eine Tabelle mit ungefähr einer Million Datensätzen, die mehrere Felder vom Typ VARCHAR enthält. Ich möchte diese in NVARCHAR ändern, aber so wie ich es verstehe, wird dies einige Zeit und Ressourcen in Anspruch nehmen, da sich die Felder in der Mitte der Tabelle befinden und SQL Server einige Kopier- / Neuordnungsvorgänge durchführen muss.

Was ist ein effizienter Weg, um dies zu erreichen?

ElHaix
quelle
2
Ist dies ein Versuch, die Datenbank zu internationalisieren? Da sich Ihre Indexschlüssel verdoppeln (AFAIR, NVARCHAR verbrauchen 2x den Speicherplatz der Varchar-Felder) und wenn Sie einen besonders langen Indexschlüssel haben, können Sie einen Schlüssel erstellen, der nicht neu erstellt werden kann (Indexschlüssel sind auf 900 Byte begrenzt, AFAIR) )
Fabricio Araujo
Und es belastet Ihr E / A-Subsystem, wenn diese Tabelle wirklich groß ist (Beispiel: eine Tabelle mit 1 Million Zeilen mit einer varchar-Spalte mit einer Zeichenfolge mit einer durchschnittlichen Größe von 40 Zeichen. Mit varchar haben Sie eine Tabelle mit 36 ​​MB char-Daten. Mit nvarchar benötigen dieselben Daten 76 MB, um gespeichert / gelesen / geschrieben zu werden.
Fabricio Araujo
Tolle Diskussion und vielen Dank für den Einblick. Die Informationen zu Indizes und Größen mit nvarchar sind am hilfreichsten. Verursacht die Verwendung von nvarchar zusätzliche Leistungstreffer gegenüber varchar?
ElHaix
3
@ FabricioAraujo: Entgleisen Sie diesen Thread bitte nicht. varchar vs nvarchar wird hier diskutiert stackackflow.com/a/198753/27535 . int vs smallint: dba.stackexchange.com/a/4979/630 . OP fragte nach dem Ändern von Feldern: Beantworten Sie diese und nehmen Sie an, dass OP weiß, was und warum er dies tun möchte. Auf Plattenstrukturen? sqlskills.com/blogs/paul/post/…
gbn
1
@ ElHaix: Ich habe deinen Kommentar in all dem Lärm vermisst. Siehe stackoverflow.com/questions/35366/… nvarchar hat in jeder Hinsicht einen erheblichen Overhead gegenüber varchar. Wenn Sie Unicode-Daten haben, verwenden Sie nvarchar.
Hacken

Antworten:

7

Bei direkter Beantwortung der Frage gibt es zwei Möglichkeiten, die Operation auszuführen.

  • Wenn die Anzahl der in der Tabelle enthaltenen Varchar-Spalten gering ist (eine oder zwei), ist es praktischer, Pseudo-temporäre Spalten zu erstellen
  • Wenn die Anzahl der Varchar-Spalten größer ist, ist der oben beschriebene Weg nicht sehr praktisch. Sie erstellen also eine Pseudotabelle. Dies wird am häufigsten für Metadatenaktualisierungsskripte einiger Datenbank-Tools wie ErWin oder ER / Studio verwendet (ich habe beide Tools verwendet und die vor der Anwendung generierten Skripte überprüft).

Hinweis zu großen Tabellen : Wenn die Tabelle nur wenige Tausend Datensätze oder weniger enthält, können Sie den Vorgang sofort ausführen. Bei Tabellen mit Millionen Datensätzen ist es praktischer, in Stapeln zu arbeiten (sagen wir jedes Mal 1000 oder 100 Datensätze).

Pseudo-temporäre Spalten

Pseudo-temporäre Spalten (ich habe vergessen, ob es einen anderen, passenderen Namen gibt) sind Spalten, in denen das Ergebnis einer Konvertierung gespeichert wird. In diesem Fall wären sie auch die letzten Spalten nach dem Prozess.

  1. Erstellen Sie die neuen Spalten mit der beabsichtigten Länge. Vergessen Sie nicht, Prüfbeschränkungen oder Standardeinstellungen in die neue Definition aufzunehmen
  2. Führen Sie ein Update durch (oder Updates, siehe obige Beobachtung), um die Daten der alten Spalte in der neuen zu speichern.
  3. Führen Sie die Protokollsicherung durch und führen Sie den Prüfpunkt durch, damit das Protokoll nicht absurd groß wird.
  4. Wenn der alten Spalte Einschränkungen zugeordnet sind, löschen Sie diese.
  5. Lass die alte Spalte fallen.
  6. Benennen Sie die neue Spalte in den alten Spaltennamen um
  7. Erstellen Sie die betroffenen Indizes neu (oder alle, wenn die betroffene Spalte auch Teil einer Einschränkung für gruppierte Primärschlüssel war - es ist selten, dass jemand einen (n) varchar als PK verwendet, aber ich habe einige gesehen).

Dies ist der gleiche Prozess, der in Aarons Antwort beschrieben wird .

Pseudo-Temp-Tabellen

Wenn sich die Änderung in mehr als einer Handvoll Spalten befindet, ist es praktischer, eine neue Tabelle basierend auf dem Schema der alten zu erstellen.

  1. Erstellen Sie eine neue Tabelle ohne Tabelleneinschränkungen (PK, FK usw.). Bringen Sie zu diesem Zeitpunkt nur Spalten mit (NICHT NULL, DEFAULT, CHECK usw.)
  2. Fügen Sie die Daten an der alten Tabelle in die neue Tabelle ein (siehe Hinweis zu großen Tabellen oben). SET IDENTITY_INSERT ist hier ein Muss.
  3. Löschen Sie nun alle Tabelleneinschränkungen (PK, FKs, Prüfungen) und Trigger für die alte Tabelle. Erstellen Sie diese Einschränkungen und Trigger für die neue Tabelle neu.
  4. Erstellen Sie alle anderen Indizes (alle auf einmal oder einzeln, abhängig von Ihrem Wartungsfenster) der alten Tabelle in der neuen Tabelle. Sofern die Tabelle keinen Clustered-Index hat, muss dies nach Schritt 3 oder zumindest nach der Erstellung der PK-Einschränkung erfolgen.
  5. Überprüfen Sie, ob alles richtig gelaufen ist (wenn Sie dabei keinen Auslöser oder eine Einschränkung vergessen haben), und löschen Sie die alte Tabelle, wenn alles in Ordnung ist.
  6. Benennen Sie die neue Tabelle in den Namen der alten Tabelle um

Hinweis zu Schritt 4 : Wenn Sie doppelte Indizes erkannt haben (das Erkennen doppelter Indizes ist ein sehr langes Thema, siehe Kimberly Tripps Blog auf SQLSkills.com), ist dies Ihre Chance, diese zu entfernen, wenn dies der Fall ist.

Auswirkungen auf die Leistung

Der Wechsel von VARCHAR zu NVARCHAR hat einige Auswirkungen auf die Leistung, zumindest für jeden SQL Server unter 2008R2. Für SQL 2008 R2 hat Aaron Bertrand einige Blog-Beiträge zur Unicode-Komprimierungsfunktion, die das Gleichgewicht ausgleichen können, wenn NVarchar-Spalten zum Speichern von Inhalten verwendet werden, die in VARCHAR-Spalten gespeichert werden können. Ich habe sie nicht vollständig gelesen, wie es die Artikel verdienen, aber das Thema ist interessant.

In NVARCHAR-Spalten (IOW, vor 2008R2) werden alle Zeichen in den Spalten mit 2 Byte pro Zeichen gespeichert. Beispielsweise wird die Zeichenfolge 'MSSQL' in 5 Bytes in einer VARCHAR-Spalte und 10 in einer NVARCHAR-Spalte gespeichert. Da Nicht-LOB-Zeichenfolgenspalten maximal 8000 Byte speichern dürfen , bedeutet dies, dass VARCHAR 8000 Zeichen speichern kann, während NVARCHR auf 4000 Zeichen beschränkt ist.

Implikationen dieser Tatsachen:

  • Da Indexschlüssel auf 900 Byte begrenzt sind (siehe Dokumente zu CREATE INDEX), schlägt der Befehl nicht fehl, wenn Sie versuchen, eine NVARCHAR (500) -Spalte zu indizieren (wenn dies die einzige Spalte im Indexschlüssel ist), sondern wenn Sie UPDATE oder Fügen Sie eine Zeile mit mehr als 450 - (Gesamtgröße anderer Spalten auf dem Indexschlüssel, falls dies der Fall ist) Zeichen ein. Der Vorgang schlägt fehl.
  • Je mehr Bytes zu betreiben sind, desto mehr Arbeit ist zu erledigen. Sie lesen / schreiben / vergleichen / cachen das Doppel von Bytes.
  • Abhängig von der Größe der Tabelle, dem Einfluss der Zeichenfolgenspalten auf die gespeicherte Größe der Tabelle und der Beteiligung der Tabelle an der Datenbankgröße können Sie eine Zunahme der (verwendeten) Datenbankgröße und aller davon betroffenen Variablen erwarten direkt oder nicht (wie Sicherungs- / Wiederherstellungszeit, Indexpflege usw.).

BEARBEITEN: Wie von gbn angegeben, lohnt es sich nicht, etwas zu erstellen, nur um VARCHARs zu verwenden, wenn Sie eine eindeutige Anforderung haben, bei der NVARCHAR-Spalten vollständig ausgefüllt werden müssen.

Fabricio Araujo
quelle
20

Ein Weg könnte sein:

  1. Fügen Sie eine NULLable NVARCHAR-Spalte hinzu
  2. Aktualisieren Sie mithilfe von Stapeln mehrere Zeilen gleichzeitig (z. B. 1000 oder 10000 Zeilen).
  3. Sichern Sie das Protokoll, Checkpoint, was haben Sie zwischen den Stapeln
  4. Wenn alle Zeilen aktualisiert wurden, löschen Sie die alte Spalte und benennen Sie die neue um
  5. Indizes neu erstellen

Dies wird auf lange Sicht nicht schneller sein und erfordert dennoch ein Wartungsfenster (da Sie nicht möchten, dass Benutzer bereits aktualisierte Zeilen aktualisieren, es sei denn, Sie setzen einen temporären Auslöser ein, um dem entgegenzuwirken), verhindert jedoch a Eine große Transaktion und nach ein paar Updates erhalten Sie mehr Vorhersehbarkeit darüber, wie lange es dauern wird.

Sie können das Gleiche tun, indem Sie eine neue Tabelle erstellen und diese umbenennen, sobald dies erledigt ist. Dies vermeidet zwar die Notwendigkeit von Schritt 5, führt jedoch zu einer noch stärkeren Datenabwanderung und kann aufgrund von Einschränkungen, Fremdschlüsseln und Triggern problematischer sein usw., die mit der Tabelle verbunden sein könnten.

Aaron Bertrand
quelle
2
Bonuspunkte für die Verwendung von Chargen. Zu viele Menschen haben diese alte und effektive Technik vergessen.
Datum
Was ist der effizienteste Weg, um eine neu hinzugefügte Spalte in NOT NULL zu ändern (nachdem sie bereits mit Werten aktualisiert wurde und sich keine Nullwerte in der Spalte befinden)? Mein Tisch hat ungefähr 50 Millionen Datensätze. Was macht SQL Server eigentlich? Überprüft es Werte und markiert die Spalte als nicht null?
Rrejc