Problem z kodowaniem w kolumnie SQL Server VARCHAR pobranej w Pythonie

10

Niedawno mieliśmy problem z kodowaniem związanym z polem, które jest przechowywane jako varchar (120) w SQL Server. W SSMS varchar pojawia się jako:

„Kto zabił JonBena?”

Jednak po wprowadzeniu do Pythona wygląda następująco:

wprowadź opis zdjęcia tutaj

Badałem to od strony Pythona i nic dziwnego się nie dzieje. Moja teoria jest taka, że ​​varchar w SQL Server akceptuje znaki UTF-8, które wyświetlają się inaczej w pythonie niż SSMS. Nie znam się dobrze na kodowaniu w SQL Server. Czy ktoś może dać mi znać, co następuje:

  • Czy jest jakiś sposób w SSMS, aby zobaczyć kodowanie varchara? Na przykład zobacz \ x82 zamiast wyświetlać przecinek, ponieważ obecnie pochodzi z SSMS?
  • Używamy SQL Server 2008. Czy istnieje sposób na zmianę kodowania dowolnych znaków UTF-8 na znaki ASCII bez użycia narzędzi do importu / eksportu lub zrzutu do płaskiego pliku? Czy mogę dokonać tej konwersji za pomocą zapytania?
  • Czy jest jakiś sposób na programową identyfikację problematycznych rekordów za pomocą zapytania (problematyczne jest definiowanie jako znaki UTF-8, które nie są obsługiwane przez ASCII)?

Z góry dziękuję!

Korzystanie sp_help N'table_name';I okazało się, że układanie tej VARCHARkolumnie jest: SQL_Latin1_General_CP1_CI_AS.

Eric
źródło
Jakiego sortowania VARCHARużywa ta kolumna?
Solomon Rutzky
@ SolomonRutzky, jak sprawdzasz sortowanie. Nie jestem pewien, co to w ogóle oznacza
Eric
Najszybszym sposobem moim zdaniem jest: sp_help N'table_name';. Spójrz na kolumnę opartą na „name”, a następnie spójrz na kolumnę „collation_name”.
Solomon Rutzky
@SolomonRutzky zestawieniem dla tego pola jest „SQL_Latin1_General_CP1_CI_AS”
Eric

Odpowiedzi:

17

SQL Server nie przechowuje UTF-8 pod żadnym pozorem. Dostajesz UTF-16 Little Endian (LE) przez NVARCHAR(w tym NCHARi NTEXT, ale nigdy nie używaj NTEXT) i XML, lub jakieś 8-bitowe kodowanie, oparte na Stronie kodowej, przez VARCHAR(w tym CHARi TEXT, ale nigdy nie używaj TEXT) .

Problem polega na tym, że Twój kod błędnie tłumaczy ten znak 0x82, sądząc, że jest to UTF-8, ale tak nie jest. Nie ma „znaku” UTF-8 o wartości 0x82, dlatego otrzymujesz symbol „nieznany” / zamienny „ ”. Zobacz poniższą tabelę UTF-8, która pokazuje, że nie ma znaku dla jednego bajtu 0x82:

Tabela kodowania UTF-8

Jak stwierdził OP, zestawienie kolumny SQL_Latin1_General_CP1_CI_AS, o której mowa, jest takie , co oznacza, że ​​8-bitowe kodowanie używa kodu Page 1252, którym jest Windows Latin 1 (ANSI) . A sprawdzenie tego wykresu (przewiń w dół do dolnego wykresu, ponieważ ma on nazwy znaków), wartość 0x82 (poszukaj „82” w kolumnie „Punkt kodowy”) jest w rzeczywistości pojedynczym nisko-9 cudzysłów , który widzisz w SSMS. Że postać, w UTF-8, to sekwencja 3 bajt: E2 80 9A.

Oznacza to wszystko: kod w języku Python musi albo ustawić kodowanie klienta dla połączenia SQL Server na stronę kodową 1252, albo trzeba zmienić / przekonwertować kodowanie zwracanego ciągu znaków z strony kodowej 1252 na UTF-8.

Oczywiście, jeśli to jest wyświetlana na stronie internetowej, to mogłoby zmienić deklarowaną charset strony, aby być Windows-1252, ale to może kolidować z innymi postaciami na stronie, jeśli istnieją znaki UTF-8 już istnieje.

Solomon Rutzky
źródło
Fajnie, to jest bardzo pomocne, dziękuję Solomon. Daj mi znać o błędnym tłumaczeniu. To dość trudny problem i nawet nie jestem pewien, od czego zacząć.
Eric
Wow, niesamowity szczegół, @Solomon! Wylądowałem tutaj, szukając innego problemu dla języka Python + MS SQL, ale czytałem dalej, ponieważ tyle się uczyłem. :-P
Mike Williamson
1
@MikeWilliamson Dzięki za udostępnienie tego komplementu :). Być może zainteresują Cię również: skróty TSQL md5 inne niż C # .NET md5 (na SO), How To Strip Hebrew Accent Marks (tutaj na DBA.SE) i Collations.Info . Cieszyć się!
Solomon Rutzky
Dzięki! Podejrzewam, że każdy, kto pracuje w języku innym niż łaciński, zna te rzeczy o wiele lepiej niż ktokolwiek z nas błogo pracujący w USA / Wielkiej Brytanii. :)
Mike Williamson
1
Tylko uwaga: MS SQL Server 2019 wprowadza natywną obsługę UTF-8 w typach danych VARCHAR / CHAR.
Gregory Arenius,