Ich habe einige SQL Server-Datenbanken geerbt. Es gibt eine Tabelle (ich nenne sie "G") mit etwa 86,7 Millionen Zeilen und 41 Spalten Breite aus einer Quellendatenbank (ich nenne sie "Q") in SQL Server 2014 Standard, auf die ETL übertragen wird eine Zieldatenbank (ich werde "P" nennen) mit demselben Tabellennamen in SQL Server 2008 R2 Standard.
dh [Q]. [G] ---> [P]. [G]
EDIT: 20.03.2017: Einige Leute haben gefragt, ob die Quelltabelle die EINZIGE Quelle zur Zieltabelle ist. Ja, es ist die einzige Quelle. Was die ETL angeht, findet keine echte Transformation statt. Eigentlich soll es eine 1: 1-Kopie der Quelldaten sein. Daher ist nicht geplant, dieser Zieltabelle zusätzliche Quellen hinzuzufügen.
Etwas mehr als die Hälfte der Spalten in [Q]. [G] sind VARCHAR (Quelltabelle):
- 13 der Spalten sind VARCHAR (80)
- 9 der Spalten sind VARCHAR (30)
- 2 der Spalten sind VARCHAR (8).
In ähnlicher Weise sind dieselben Spalten in [P]. [G] NVARCHAR (Zieltabelle) mit derselben Anzahl von Spalten mit derselben Breite. (Mit anderen Worten, gleiche Länge, aber NVARCHAR).
- 13 der Spalten sind NVARCHAR (80)
- 9 der Spalten sind NVARCHAR (30)
- 2 der Spalten sind NVARCHAR (8).
Das ist nicht mein Design.
Ich möchte die Datentypen der Spalten [P]. [G] (Ziel) von NVARCHAR nach VARCHAR ÄNDERN. Ich möchte es sicher machen (ohne Datenverlust durch Konvertierung).
Wie kann ich anhand der Datenwerte in jeder NVARCHAR-Spalte in der Zieltabelle feststellen, ob die Spalte tatsächlich Unicode-Daten enthält oder nicht?
Eine Abfrage (DMVs?), Die jeden Wert (in einer Schleife?) Jeder NVARCHAR-Spalte überprüft und angibt, ob einer der Werte echtes Unicode ist, ist die ideale Lösung. Andere Methoden sind jedoch willkommen.
quelle
[G]
werden an ETL übergeben[P]
. Wenn dies der Fall[G]
istvarchar
und der ETL-Prozess der einzige Weg ist, auf dem Daten[P]
eingehen, sollte es keinen geben, sofern der Prozess keine echten Unicode-Zeichen hinzufügt. Wenn andere Prozesse Daten hinzufügen oder ändern[P]
, müssen Sie vorsichtiger sein - nur weil alle aktuellen Daten vorhanden sein können,varchar
bedeutet dies nicht, dassnvarchar
Daten morgen nicht hinzugefügt werden können. Ebenso ist es möglich, dass alles, was Daten verbraucht, Daten[P]
benötigtnvarchar
.Antworten:
Angenommen, eine Ihrer Spalten enthält keine Unicode-Daten. Um sicherzustellen, dass Sie den Spaltenwert für jede Zeile lesen müssen. Sofern Sie keinen Index für die Spalte haben, müssen Sie bei einer Rowstore-Tabelle jede Datenseite aus der Tabelle lesen. Vor diesem Hintergrund halte ich es für sehr sinnvoll, alle Spaltenprüfungen in einer einzigen Abfrage für die Tabelle zu kombinieren. Auf diese Weise werden Sie die Daten der Tabelle nicht viele Male lesen und müssen keinen Cursor oder eine andere Art von Schleife codieren.
Um eine einzelne Spalte zu überprüfen, gehen Sie folgendermaßen vor:
Ein Cast von
NVARCHAR
bisVARCHAR
sollte dasselbe Ergebnis liefern, außer wenn es Unicode-Zeichen gibt. Unicode-Zeichen werden in konvertiert?
. Daher sollte der obige Code dieNULL
Fälle korrekt behandeln. Sie müssen 24 Spalten überprüfen, sodass Sie jede Spalte in einer einzelnen Abfrage mithilfe von skalaren Aggregaten überprüfen können. Eine Implementierung ist unten:Für jede Spalte erhalten Sie das Ergebnis,
1
ob einer der Werte Unicode enthält. Das Ergebnis von0
bedeutet, dass alle Daten sicher konvertiert werden können.Es wird dringend empfohlen, eine Kopie der Tabelle mit den neuen Spaltendefinitionen zu erstellen und Ihre Daten dort zu kopieren. Sie werden teure Konvertierungen durchführen, wenn Sie dies direkt tun, sodass das Erstellen einer Kopie möglicherweise nicht so viel langsamer ist. Wenn Sie über eine Kopie verfügen , können Sie auf einfache Weise überprüfen, ob alle Daten noch vorhanden sind (eine Möglichkeit ist die Verwendung des Schlüsselworts EXCEPT ), und Sie können den Vorgang sehr einfach rückgängig machen.
Beachten Sie außerdem, dass Sie derzeit möglicherweise keine Unicode-Daten haben. Möglicherweise lädt eine zukünftige ETL Unicode in eine zuvor bereinigte Spalte. Wenn dies in Ihrem ETL-Prozess nicht überprüft wird, sollten Sie dies hinzufügen, bevor Sie diese Konvertierung durchführen.
quelle
NVARCHAR
Spalte nicht konvertiert werden muss,NVARCHAR
da es sich bereits um diesen Typ handelt. Und nicht sicher, wie Sie das nicht konvertierbare Zeichen ermittelt haben, aber Sie können die Spalte konvertierenVARBINARY
, um die UTF-16-Byte-Sequenzen zu erhalten. Und UTF-16 ist die umgekehrte Bytereihenfolge, alsop
=0x7000
und dann kehren Sie diese beiden Bytes um, um den Codepunkt zu erhaltenU+0070
. Wenn die Quelle jedoch VARCHAR ist, kann es sich nicht um ein Unicode-Zeichen handeln. Etwas anderes ist los. Benötigen Sie weitere Informationen.VARCHAR
implizit konvertiert wirdNVARCHAR
, aber es ist möglicherweise besser, dies zu tunCONVERT(NVARCHAR(80), CONVERT(VARCHAR(80), column)) <> column
.SUBSTRING
Manchmal funktioniert es, aber es funktioniert nicht mit Zusatzzeichen, wenn Kollatierungen verwendet werden, die nicht auf enden_SC
, und die, die John verwendet, funktioniert nicht, obwohl dies hier wahrscheinlich kein Problem darstellt. Die Konvertierung in VARBINARY funktioniert jedoch immer. UndCONVERT(VARCHAR(10), CONVERT(NVARCHAR(10), '›'))
führt nicht dazu?
, also würde ich die Bytes sehen wollen. Der ETL-Prozess hat ihn möglicherweise konvertiert.Bevor Sie etwas unternehmen, sollten Sie die von @RDFozz gestellten Fragen in einem Kommentar zur Frage berücksichtigen, nämlich:
Wenn die Antwort nicht "Ich bin zu 100% sicher, dass dies die einzige Datenquelle für diese Zieltabelle ist" lautet , nehmen Sie keine Änderungen vor, unabhängig davon, ob die aktuell in der Tabelle enthaltenen Daten konvertiert werden können oder nicht Datenverlust.
Und ich möchte eine verwandte Frage hinzufügen: Gab es eine Diskussion darüber, ob mehrere Sprachen in der aktuellen Quelltabelle (dh
[Q].[G]
) unterstützt werden, indem sie in konvertiert werdenNVARCHAR
?Sie müssen herumfragen, um ein Gefühl für diese Möglichkeiten zu bekommen. Ich gehe davon aus, dass Ihnen derzeit nichts gesagt wurde, was in diese Richtung weisen würde, ansonsten würden Sie diese Frage nicht stellen. Wenn jedoch angenommen wurde, dass diese Fragen "nein" lauten, müssen sie von a gestellt und gestellt werden breit genug Publikum, um die genaueste / vollständigste Antwort zu erhalten.
Das Hauptproblem hierbei ist nicht so sehr, dass Unicode-Codepunkte (jemals) nicht konvertiert werden können , sondern vielmehr, dass Codepunkte nicht alle auf eine einzelne Codepage passen. Das ist das Schöne an Unicode: Es kann Zeichen aus ALLEN Codepages enthalten. Wenn Sie von
NVARCHAR
- wo Sie sich nicht um Codepages kümmern müssen - nach konvertieren , müssenVARCHAR
Sie sicherstellen, dass für die Sortierung der Zielspalte dieselbe Codepage wie für die Quellspalte verwendet wird. Dies setzt voraus, dass entweder eine Quelle oder mehrere Quellen dieselbe Codepage verwenden (jedoch nicht unbedingt dieselbe Kollatierung). Wenn jedoch mehrere Quellen mit mehreren Codepages vorhanden sind, kann möglicherweise das folgende Problem auftreten:Rückgabe (2. Ergebnismenge):
Wie Sie sehen können, alle diese Zeichen können konvertieren
VARCHAR
, nur nicht in der gleichenVARCHAR
Spalte.Verwenden Sie die folgende Abfrage, um die Codepage für jede Spalte Ihrer Quelltabelle zu bestimmen:
DAS GESAGT WERDEN ....
Sie haben erwähnt, dass Sie unter SQL Server 2008 R2 arbeiten, aber Sie haben nicht gesagt, welche Edition. WENN Sie sich zufällig in der Enterprise Edition befinden, vergessen Sie all dieses Konvertierungsmaterial (da Sie es wahrscheinlich nur tun, um Speicherplatz zu sparen) und aktivieren Sie die Datenkomprimierung:
Implementierung der Unicode-Komprimierung
Wenn Sie die Standard Edition verwenden (und es scheint, dass Sie you sind), gibt es eine weitere Möglichkeit, die noch zu lange reicht: ein Upgrade auf SQL Server 2016, da SP1 die Möglichkeit enthält, dass alle Editionen die Datenkomprimierung verwenden (denken Sie daran, ich habe "Long Shot" gesagt) "😉).
Nachdem jetzt klargestellt wurde, dass es nur eine Quelle für die Daten gibt, müssen Sie sich natürlich keine Sorgen mehr machen, da die Quelle keine reinen Unicode-Zeichen oder Zeichen außerhalb ihres spezifischen Codes enthalten darf Seite. In diesem Fall sollten Sie nur die gleiche Kollatierung wie die Quellenspalte verwenden oder mindestens eine, die dieselbe Codepage verwendet. Das heißt, wenn die Quellenspalte verwendet wird
SQL_Latin1_General_CP1_CI_AS
, können Sie sieLatin1_General_100_CI_AS
am Ziel verwenden.Sobald Sie wissen, welche Sortierung verwendet werden soll, können Sie entweder:
ALTER TABLE ... ALTER COLUMN ...
zu seinVARCHAR
(achten Sie darauf, die aktuelleNULL
/NOT NULL
Einstellung anzugeben ), was ein wenig Zeit und viel Transaktionsprotokollspeicher für 87 Millionen Zeilen erfordert, ODERErstellen Sie für jede Spalte neue "ColumnName_tmp" -Spalten und füllen Sie diese langsam über "
UPDATE
doing" ausTOP (1000) ... WHERE new_column IS NULL
. Sobald alle Zeilen ausgefüllt sind (und überprüft wurden, dass sie alle korrekt kopiert wurden!), Müssen Sie möglicherweise einen Trigger zum Behandeln von UPDATEs (sofern vorhanden) in einer expliziten Transaktion verwendensp_rename
, um die Spaltennamen der "aktuellen" zu verwendenden Spalten auszutauschen. " _Old "und dann die neuen Spalten" _tmp ", um einfach das" _tmp "aus den Namen zu entfernen. Rufen Sie dannsp_reconfigure
die Tabelle auf, um zwischengespeicherte Pläne, die auf die Tabelle verweisen, ungültig zu machen. Wenn Ansichten vorhanden sind, die auf die Tabelle verweisen, müssen Sie sie aufrufensp_refreshview
(oder so ähnlich). Sobald Sie die App validiert haben und die ETL ordnungsgemäß damit funktioniert, können Sie die Spalten löschen.quelle
Latin1_General_100_CI_AS
viel besser ist als die, die Sie verwenden. Das bedeutet, dass das Sortier- und Vergleichsverhalten zwischen ihnen gleich ist, auch wenn es nicht so gut ist wie die neuere Sortierung, die ich gerade erwähnt habe.Ich habe einige Erfahrungen damit von damals, als ich einen richtigen Job hatte. Da ich zu der Zeit die Basisdaten beibehalten wollte und auch neue Daten berücksichtigen musste, bei denen möglicherweise Zeichen verloren gingen, entschied ich mich für eine nicht persistierte berechnete Spalte.
Hier ist ein kurzes Beispiel für die Verwendung einer Kopie der Super User-Datenbank aus dem SO-Daten-Dump .
Wir können sofort erkennen, dass es DisplayNames mit Unicode-Zeichen gibt:
Fügen wir also eine berechnete Spalte hinzu, um herauszufinden, wie viele! Die Spalte DisplayName lautet
NVARCHAR(40)
.Die Anzahl gibt ~ 3000 Zeilen zurück
Der Ausführungsplan ist allerdings ein bisschen langweilig. Die Abfrage wird schnell beendet, aber dieser Datensatz ist nicht besonders groß.
Da berechnete Spalten nicht beibehalten werden müssen, um einen Index hinzuzufügen, können wir eine der folgenden Aktionen ausführen:
Was uns einen etwas ordentlicheren Plan gibt:
Ich verstehe, wenn das nicht ist die Antwort ist, da es sich um architektonische Änderungen handelt. Angesichts der Größe der Daten möchten Sie jedoch wahrscheinlich Indizes hinzufügen, um Abfragen zu bewältigen, die sich selbst in die Tabelle eintragen.
Hoffe das hilft!
quelle
Anhand des Beispiels unter So überprüfen Sie, ob ein Feld Unicode-Daten enthält , können Sie die Daten in jeder Spalte lesen und die folgenden
CAST
Schritte ausführen und überprüfen:quelle