Odziedziczyłem niektóre bazy danych SQL Server. Jest jedna tabela (nazywam „G”), z około 86,7 milionami wierszy i 41 kolumnami, ze źródłowej bazy danych (nazywam „Q”) w SQL Server 2014 Standard, która przechodzi do ETL docelowa baza danych (nazywam „P”) o tej samej nazwie tabeli w SQL Server 2008 R2 Standard.
tj. [Q]. [G] ---> [P]. [G]
EDYCJA: 3/20/2017: Niektóre osoby zapytały, czy tabela źródłowa jest TYLKO źródłem tabeli docelowej. Tak, jest to jedyne źródło. Jeśli chodzi o ETL, nie zachodzi żadna prawdziwa transformacja; w rzeczywistości ma to być kopia danych źródłowych 1: 1. Dlatego nie ma planów dodawania dodatkowych źródeł do tej tabeli docelowej.
Nieco ponad połowa kolumn w [Q]. [G] to VARCHAR (tabela źródłowa):
- 13 kolumn to VARCHAR (80)
- 9 kolumn to VARCHAR (30)
- 2 kolumny to VARCHAR (8).
Podobnie, te same kolumny w [P]. [G] to NVARCHAR (tabela docelowa), z tą samą liczbą kolumn o tych samych szerokościach. (Innymi słowy, ta sama długość, ale NVARCHAR).
- 13 kolumn to NVARCHAR (80)
- 9 kolumn to NVARCHAR (30)
- 2 kolumny to NVARCHAR (8).
To nie jest mój projekt.
Chciałbym ZMIENIĆ [P]. [G] (docelowe) typy danych kolumn od NVARCHAR do VARCHAR. Chcę to zrobić bezpiecznie (bez utraty danych z konwersji).
Jak mogę spojrzeć na wartości danych w każdej kolumnie NVARCHAR w tabeli docelowej, aby potwierdzić, czy kolumna rzeczywiście zawiera dane Unicode?
Zapytanie (DMVs?), Które może sprawdzić każdą wartość (w pętli?) Każdej kolumny NVARCHAR i powiedzieć mi, czy DOWOLNA z wartości jest oryginalnym Unicode, byłoby idealnym rozwiązaniem, ale inne metody są mile widziane.
źródło
[G]
są przesyłane do ETL[P]
. Jeśli[G]
takvarchar
, a proces ETL jest jedynym sposobem na wejście danych[P]
, to chyba że proces doda prawdziwe znaki Unicode, nie powinno być żadnych. Jeśli inne procesy dodają lub modyfikują dane[P]
, musisz być bardziej ostrożny - tylko dlatego, że wszystkie obecne danevarchar
nie oznaczają, żenvarchar
nie można dodać danych jutro. Podobnie możliwe jest, że cokolwiek konsumuje dane w[P]
potrzebujenvarchar
danych.Odpowiedzi:
Załóżmy, że jedna z kolumn nie zawiera żadnych danych Unicode. Aby sprawdzić, czy musisz odczytać wartość kolumny dla każdego wiersza. O ile nie masz indeksu w kolumnie, z tabelą magazynu wierszy będziesz musiał odczytać każdą stronę danych z tabeli. Mając to na uwadze, myślę, że sensowne jest połączenie wszystkich kontroli kolumn w jedno zapytanie względem tabeli. W ten sposób nie będziesz czytał danych tabeli wiele razy i nie będziesz musiał kodować kursora ani innej pętli.
Aby sprawdzić jedną kolumnę, uwierz, że możesz to po prostu zrobić:
Rzut z
NVARCHAR
naVARCHAR
powinien dać taki sam wynik, chyba że występują znaki Unicode. Znaki Unicode zostaną przekonwertowane na?
. Tak więc powyższy kod powinienNULL
poprawnie obsługiwać przypadki. Masz 24 kolumny do sprawdzenia, więc sprawdzasz każdą kolumnę w jednym zapytaniu, używając agregatów skalarnych. Jedna implementacja jest poniżej:Dla każdej kolumny otrzymasz wynik,
1
jeśli którakolwiek z jej wartości zawiera Unicode. Dzięki0
temu wszystkie dane można bezpiecznie konwertować.Zdecydowanie zalecam wykonanie kopii tabeli z nowymi definicjami kolumn i skopiowanie tam danych. Będziesz robił drogie konwersje, jeśli zrobisz to na miejscu, więc tworzenie kopii może nie być o wiele wolniejsze. Posiadanie kopii oznacza, że możesz łatwo sprawdzić, czy wszystkie dane nadal tam są (jednym ze sposobów jest użycie słowa kluczowego EXCEPT ) i możesz bardzo łatwo cofnąć operację.
Pamiętaj też, że obecnie nie możesz mieć żadnych danych Unicode. Możliwe, że przyszły ETL może załadować Unicode do poprzednio czystej kolumny. Jeśli nie ma zaznaczenia tego w procesie ETL, powinieneś rozważyć dodanie tego przed wykonaniem tej konwersji.
źródło
NVARCHAR
kolumny,NVARCHAR
ponieważ jest to już ten typ. Nie jestem pewien, w jaki sposób ustalono nieprzekształcalny znak, ale można przekonwertować kolumnę,VARBINARY
aby uzyskać sekwencje bajtów UTF-16. A UTF-16 jest odwrotną kolejnością bajtów, więcp
=0x7000
a następnie odwracasz te dwa bajty, aby uzyskać kod punktuU+0070
. Ale jeśli źródłem jest VARCHAR, to nie może być znakiem Unicode. Dzieje się coś jeszcze. Potrzebujesz więcej informacji.VARCHAR
domyślnie zostanie przekonwertowane naNVARCHAR
, ale może być lepiejCONVERT(NVARCHAR(80), CONVERT(VARCHAR(80), column)) <> column
.SUBSTRING
czasami działa, ale nie działa z postaciami uzupełniającymi, gdy używa się kolacji, które się nie kończą_SC
, a ta, której używa John, nie działa, choć prawdopodobnie nie jest to problem. Ale konwersja na VARBINARY zawsze działa. ICONVERT(VARCHAR(10), CONVERT(NVARCHAR(10), '›'))
nie powoduje?
, więc chciałbym zobaczyć bajty. Proces ETL mógł go przekonwertować.Zanim cokolwiek zrobisz, rozważ pytania postawione przez @RDFozz w komentarzu do pytania, a mianowicie:
Jeśli odpowiedź jest inna niż „Jestem w 100% pewien, że jest to jedyne źródło danych dla tej docelowej tabeli”, nie wprowadzaj żadnych zmian, niezależnie od tego, czy dane znajdujące się obecnie w tabeli mogą zostać przekonwertowane bez utrata danych.
I chciałbym dodać powiązane pytanie: Czy była jakakolwiek dyskusja wokół obsługi wielu języków w tabeli źródła prądu (tj
[Q].[G]
) poprzez przekształcenie go doNVARCHAR
?Będziesz musiał zapytać, aby poznać te możliwości. Zakładam, że obecnie nie powiedziano ci niczego, co wskazywałoby w tym kierunku, inaczej nie zadawałbyś tego pytania, ale jeśli założono, że pytania te brzmią „nie”, należy je zadać i zadać wystarczająco szeroka publiczność, aby uzyskać najbardziej dokładną / kompletną odpowiedź.
Głównym problemem tutaj jest nie tyle posiadanie punktów kodowych Unicode, których nie można przekonwertować (kiedykolwiek), ale przede wszystkim posiadanie punktów kodowych, które nie wszystkie pasują do jednej strony kodowej. To miła rzecz w Unicode: może przechowywać znaki ze WSZYSTKICH stron kodowych. Jeśli konwertujesz z
NVARCHAR
- gdzie nie musisz się martwić o strony kodowe - naVARCHAR
, to musisz upewnić się, że sortowanie w docelowej kolumnie używa tej samej strony kodowej, co w kolumnie źródłowej. Zakłada się, że ma ono jedno źródło lub wiele źródeł korzystających z tej samej strony kodowej (choć niekoniecznie tego samego sortowania). Ale jeśli istnieje wiele źródeł z wieloma stronami kodowymi, możesz potencjalnie napotkać następujący problem:Zwraca (2. zestaw wyników):
Jak widać, wszystkie z tych znaków można przekonwertować
VARCHAR
, ale nie w tej samejVARCHAR
kolumnie.Użyj następującego zapytania, aby ustalić, jaka jest strona kodowa dla każdej kolumny tabeli źródłowej:
TO POWIEDZIAŁO ....
Wspomniałeś, że jesteś na SQL Server 2008 R2, ALE, nie powiedziałeś, która edycja. JEŚLI akurat korzystasz z wersji Enterprise Edition, zapomnij o tych wszystkich rzeczach związanych z konwersją (ponieważ prawdopodobnie robisz to tylko w celu zaoszczędzenia miejsca) i włącz kompresję danych:
Implementacja kompresji Unicode
Jeśli używasz Wersji standardowej (a teraz wydaje się, że masz 😞), istnieje jeszcze jedna długa szansa: uaktualnienie do SQL Server 2016, ponieważ dodatek SP1 umożliwia wszystkim wersjom korzystanie z kompresji danych (pamiętaj, powiedziałem „dalekie ujęcie” „😉).
Oczywiście teraz, gdy zostało już wyjaśnione, że istnieje tylko jedno źródło danych, nie masz się czym martwić, ponieważ źródło nie może zawierać żadnych znaków tylko Unicode lub znaków poza określonym kodem strona. W takim przypadku jedyną rzeczą, na którą powinieneś zwrócić uwagę, jest użycie tego samego sortowania co kolumny źródłowej lub przynajmniej takiego, który używa tej samej strony kodowej. Oznacza to, że jeśli używasz kolumny źródłowej
SQL_Latin1_General_CP1_CI_AS
, możesz użyć jejLatin1_General_100_CI_AS
w miejscu docelowym.Gdy już wiesz, z którego sortowania korzystać, możesz:
ALTER TABLE ... ALTER COLUMN ...
byćVARCHAR
(należy podać bieżąceNULL
/NOT NULL
ustawienie), co wymaga trochę czasu i dużo miejsca w dzienniku transakcji dla 87 milionów wierszy, LUBUtwórz nowe kolumny „ColumnName_tmp” dla każdej z nich i powoli wypełniaj poprzez
UPDATE
wykonanieTOP (1000) ... WHERE new_column IS NULL
. Po wypełnieniu wszystkich wierszy (i sprawdzeniu, czy wszystkie zostały poprawnie skopiowane! Może być potrzebny wyzwalacz do obsługi UPDATE, jeśli istnieją), w jawnej transakcji, użyjsp_rename
do zamiany nazw kolumn „bieżących” kolumn na „ _Old ”, a następnie nowe kolumny„ _tmp ”, aby po prostu usunąć„ _tmp ”z nazw. Następnie wywołajsp_reconfigure
tabelę, aby unieważnić wszelkie buforowane plany odwołujące się do tabeli, a jeśli są jakieś widoki odwołujące się do tabeli, musisz zadzwonićsp_refreshview
(lub coś w tym rodzaju). Po sprawdzeniu poprawności aplikacji i ETL działa z nią poprawnie, możesz upuścić kolumny.źródło
Latin1_General_100_CI_AS
jest znacznie lepsze niż to, którego używasz. Łatwe oznaczenie, że sortowanie i porównywanie będzie między nimi takie samo, nawet jeśli nie tak dobre, jak w nowszym zestawieniu, o którym właśnie wspomniałem.Mam z tym pewne doświadczenie, kiedy miałem prawdziwą pracę. Ponieważ w tym czasie chciałem zachować dane podstawowe, a także musiałem uwzględnić nowe dane, które mogą zawierać znaki, które zgubiłyby się w tasowaniu, wybrałem kolumnę obliczeniową, która nie przetrwała.
Oto szybki przykład użycia kopii bazy danych Super User z zrzutu danych SO .
Od razu widzimy, że istnieją DisplayNames ze znakami Unicode:
Dodajmy więc kolumnę obliczeniową, aby dowiedzieć się, ile! Kolumna DisplayName to
NVARCHAR(40)
.Liczba zwraca ~ 3000 wierszy
Plan wykonania jest jednak trochę trudny. Zapytanie kończy się szybko, ale ten zestaw danych nie jest strasznie duży.
Ponieważ kolumny obliczane nie muszą być utrwalane w celu dodania indeksu, możemy wykonać jedną z następujących czynności:
Co daje nam nieco bardziej uporządkowany plan:
Rozumiem, że jeśli nie jest to odpowiedź, ponieważ wiąże się to ze zmianami architektonicznymi, ale biorąc pod uwagę rozmiar danych, prawdopodobnie próbujesz dodać indeksy, aby poradzić sobie z zapytaniami, które i tak same dołączają do tabeli.
Mam nadzieję że to pomoże!
źródło
Korzystając z przykładu Jak sprawdzić, czy pole zawiera dane Unicode , możesz odczytać dane w każdej kolumnie i wykonać
CAST
poniższe czynności:źródło