Dlaczego typ danych varchar zezwala na wartości Unicode?

17

Mam tabelę z kolumną varchar. Pozwala na znak towarowy (™), prawa autorskie (©) i inne znaki Unicode, jak pokazano poniżej.

Create table VarcharUnicodeCheck
(
col1 varchar(100)
)

insert into VarcharUnicodeCheck (col1) values ('MyCompany')
insert into VarcharUnicodeCheck (col1) values ('MyCompany™')
insert into VarcharUnicodeCheck (col1) values ('MyCompany░')
insert into VarcharUnicodeCheck (col1) values ('MyCompanyï')
insert into VarcharUnicodeCheck (col1) values ('MyCompany')

select * from VarcharUnicodeCheck

Ale definicja varchar mówi, że pozwala na dane łańcuchowe inne niż Unicode. Ale symbole znaków towarowych (™) i zarejestrowanych (®) są znakami Unicode . Czy definicja jest sprzeczna z właściwością typu danych varchar? Przeczytałem kilka linków, takich jak pierwszy i drugi . Ale nadal nie mogłem zrozumieć, dlaczego dopuszcza ciąg znaków Unicode, gdy definicja mówi, że dopuszcza tylko wartości łańcuchów innych niż Unicode.

siedmiodniowa żałoba
źródło
12
Wszystkie znaki są znakami Unicode.
Martin Smith
Microsoft często używa UNICODE, gdy mają na myśli UTF-16 / UCS-2. Więc mogą nawet nie liczyć UTF-8, ponieważ UNICODE to jakiś kontekst.
CodesInChaos
1
@CodesInChaos: Z trudem analizowałem twój komentarz, ale martwię się, że mylisz Unicode z różnymi kodowaniami UTF-n.
Lekkość ściga się z Monicą
1
@Martin Smith: Jeśli wszystkie znaki są znakami Unicode, to dlaczego robi to Microsoft definicja varchar mówi, że zezwala na dane ciągów innych niż Unicode?
Shiva
2
kodowanie znaków w varchar nie jest Unicode, ale wszystkie znaki istnieją w Unicode
Martin Smith

Odpowiedzi:

15

Ale symbole znaków towarowych (™) i zarejestrowanych (®) są znakami Unicode.

Mylisz się tutaj. Twoje ciągi zawierają tylko asciiznaki.

Oto prosty test, który pokazuje, że wszystkie twoje postacie są ascii (+ niektóre extended asciiz kodami ascii między 128 a 255):

declare @VarcharUnicodeCheck table
(
col1 varchar(100)
)

insert into @VarcharUnicodeCheck (col1) values ('MyCompany')
insert into @VarcharUnicodeCheck (col1) values ('MyCompany™')
insert into @VarcharUnicodeCheck (col1) values ('MyCompany░')
insert into @VarcharUnicodeCheck (col1) values ('MyCompanyï')
insert into @VarcharUnicodeCheck (col1) values ('MyCompany')

select *,
        right(col1, 1)as last_char, 
        ascii(right(col1, 1)) as_last_char_ascii
from @VarcharUnicodeCheck;

Tutaj możesz wyraźnie zobaczyć, że wszystkie twoje postacie są kodowane 1-bajtowo:

wprowadź opis zdjęcia tutaj

Tak, nie są to czyste znaki ascii, ale są rozszerzonymi ASCII .

Tutaj pokazuję ci prawdziwy znak Unicode Trademark(™)oraz jego kod i reprezentację binarną:

declare @t table (uni_ch nchar(1), ascii_ch char(1));
insert into @t values (N'™', '™');

select unicode(uni_ch) as [unicode of ™], 
       ascii(ascii_ch) [ascii of ™], 
       cast(uni_ch as varbinary(10)) as [uni_ch as varbinary], 
       cast(ascii_ch as varbinary(10)) as [ascii_ch as varbinary]
from @t;

wprowadź opis zdjęcia tutaj

Wreszcie możesz zobaczyć, że Trademark(™)znak Unicode ma kod 8482, a nie 153:

select nchar(8482), nchar(153)
sepupiczny
źródło
1
Ale w wspomnianym artykule nie ma słowa „ASCII”, mówią one tylko o znakach Unicode i innych niż Unicode, a znak towarowy (™), którego użyłeś, nie był Unicode.
sepupiczny
16
„Rozszerzony ASCII” to strasznie dwuznaczny termin. Bardziej pomocne byłoby przyjrzenie się, jakie kodowanie 8-bitowe faktycznie jest używane (czy jest oparte na ustawieniach regionalnych / sortowania?). Zgaduję stronę kodową Windows 1252 , która rzeczywiście koduje ™ jako znak 153.
IMSoP
2
@sepupic Myślę, że musisz przeczytać więcej o różnicy między punktami kodowymi a kodowaniem. Wikipedia może pomóc. „Kodowanie odwzorowuje (prawdopodobnie podzbiór) zakres punktów kodu Unicode na sekwencje wartości w pewnym zakresie o stałej wielkości, zwanym wartościami kodu ”. 8482 jest punktem kodowym dla ™, który można zakodować jako \ x99 (153) w Windows-1252, jako \ xAA w MacRoman, jako \ xE2 \ x84 \ xA2 w UTF-8 itp.
ciekawynii 30.01.18
7
Należy zachować ostrożność przy znakach 8-bitowych powyżej 127: to, co reprezentuje każdy kod powyżej 127, może i będzie się zmieniać w zależności od używanego kodowania, które będzie się różnić w zależności od używanego sortowania. Na stronie kodowej 1252 unicode 8482 jest odwzorowany na 153. Na stronie kodowej 850 to miejsce zajmuje 214 ( Ö), a na ISO-8859-1 (czasami nazywany Latin1) jest to kod kontrolny bez reprezentacji do wydrukowania. Jeśli nie wiesz , że zawsze będziesz używać tej samej strony kodowej, bezpieczniej jest trzymać się znaków ANSI (127 lub mniej) lub używać typów Unicode. Strona kodowa 1252 jest najczęstsza w SQL Server, ale daleka od wszechobecności.
David Spillett
4
@Shiva Absolutne minimum Każdy programista absolutnie, pozytywnie musi wiedzieć o Unicode i zestawach znaków . ASCII jest podzbiorem wielu kodowań i prawie wszystkie z nich zawierają symbole inne niż ASCII i jednocześnie nie są Unicode. A Unicode ma również wiele różnych kodowań (takich jak UTF-8, UTF-32 itp.).
jpmc26
7

Z komentarzy zgadzam się, że „rozszerzony ASCII” to naprawdę zły termin, który w rzeczywistości oznacza stronę kodową, która odwzorowuje znaki / punkty kodowe w zakresie 128-255, poza standardowym zakresem punktów kodowych 0-127 zdefiniowanym przez ASCII.

SQL Server obsługuje wiele stron kodowych poprzez sortowanie. Znaki inne niż ASCII mogą być przechowywane w varchar, o ile podstawowe zestawienie obsługuje znak.

Znak „™” może być przechowywany w kolumnach varchar / char, gdy strona kodowa sortowania programu SQL Server ma 1250 lub więcej. Poniżej znajduje się zapytanie:

SELECT COLLATIONPROPERTY(name, 'CodePage') AS code_page, name, description
FROM sys.fn_helpcollations()
WHERE COLLATIONPROPERTY(name, 'CodePage') >= 1250
ORDER BY name;

Ale tylko ich podzbiór obsługuje również znak „©”, więc sortowanie kolumn musi być jednym z następujących, aby obsługiwać oba:

SELECT COLLATIONPROPERTY(name, 'CodePage') AS code_page, name, description
FROM sys.fn_helpcollations()
WHERE COLLATIONPROPERTY(name, 'CodePage') IN(
    1250
    ,1251
    ,1252
    ,1253
    ,1254
    ,1255
    ,1256
    ,1257
    ,1258
)
ORDER BY name;
Dan Guzman
źródło
4

Ale definicja varchar mówi, że zezwala na dane łańcuchowe inne niż Unicode . Ale symbole znaków towarowych (™) i zarejestrowanych (®) są znakami Unicode . Czy definicja jest sprzeczna z właściwością typu danych varchar?

Podczas gdy inne odpowiedzi nie są niepoprawne, myślę, że pomogłoby to wskazać na pomyłkę w podstawowej terminologii. Podkreśliłem dwa słowa w powyższym cytacie z pytania jako przykład tego zamieszania. Kiedy dokumentacja SQL Server mówi o Unicode i non-Unicode danych , są one nie mówić o znaki . Mówią o sekwencjach bajtów reprezentujących określone znaki. Podstawowa różnica między typami Unicode ( NCHAR, NVARCHAR, XML, a przestarzałe / zła NTEXT) i typami non-Unicode ( CHAR, VARCHAR, a przestarzałe / zła TEXT) jest tym, co rodzaje sekwencji bajtów mogą przechowywać.

Typy inne niż Unicode przechowują jedno z kilku 8-bitowych kodowań, podczas gdy typy Unicode przechowują pojedyncze 16-bitowe kodowanie Unicode: UTF-16 Little Endian. Jak wspomniano w innych odpowiedziach, które znaki mogą być przechowywane w kodowaniu 8-bitowym / innym niż Unicode, zależy od strony kodowej, która jest określona przez sortowanie. Podczas gdy inni zauważyli, że wartość bajtu „znaku” może się różnić w zależności od stron kodowych, na których się znajduje, wartość bajtu może nawet różnić się w obrębie tej samej strony kodowej w przypadku jednej z kilku stron kodowych EBCDIC (odmiany systemu Windows- 1252), które można znaleźć tylko w starszych, nie należy tak naprawdę używać kolacji SQL Server (tj. Tych, których nazwy zaczynają się od SQL_).

Dlatego definicja jest dokładna: wszystkie znaki, które możesz przechowywać w typie innym niż Unicode, są zawsze 8-bitowe (nawet jeśli używają dwóch 8-bitowych wartości w kombinacji jako pojedynczego „znaku”, co właśnie Double- Zestaw znaków bajtów / strony kodowe DBCS pozwalają na). A typy danych Unicode są zawsze 16-bitowe, nawet jeśli czasami używają dwóch 16-bitowych wartości w kombinacji jako pojedynczego „znaku” (tj. Pary zastępczej, która z kolei reprezentuje znak uzupełniający).

ORAZ ze względu na natywną obsługę SQL Server kodowania UTF-8 VARCHARi CHARtypów danych od SQL Server 2019,

VARCHARnie może być dłużej określany jako „inny niż Unicode”. Począwszy od pierwszej publicznej wersji beta programu SQL Server 2019 we wrześniu 2018 r., Powinniśmy nazywać go VARCHAR„8-bitowym typem danych”, nawet jeśli mówimy o wersjach wcześniejszych niż SQL Server 2019. Ta terminologia obowiązuje w przypadku wszystkich 4 typów kodowań, których można używać z VARCHAR:

  1. Rozszerzony ASCII
  2. Zestawy znaków dwubajtowych (DBCS)
  3. EBCDIC
  4. UTF-8 (Unicode)

Tylko TEXTtyp danych (przestarzały od SQL Server 2005, więc nie używaj go) jest „inny niż Unicode”, ale to tylko kwestia techniczna, a określenie go jako „typ danych 8-bitowych” jest dokładne.

NVARCHAR, NCHARi NTEXTmoże być określany jako „UTF-16” lub „16-bitowy typ danych”. Wierzę, że Oracle używa terminologii „tylko Unicode” NVARCHAR, ale nie wyklucza to wyraźnie możliwości użycia UTF-8 (również kodowania Unicode), co nie będzie działać, więc prawdopodobnie najlepiej trzymać się dwie pierwsze opcje.

Szczegółowe informacje na temat nowych kodowań UTF-8 znajdują się w moim poście:

Natywne wsparcie UTF-8 w SQL Server 2019: Zbawiciel czy fałszywy prorok?

PS Powoli pracuję nad aktualizacją dokumentacji SQL Server, aby odzwierciedlić te zmiany.

PPS Microsoft zaktualizował już niektóre strony o informacje UTF-8, w tym dokumentację char i varchar wymienioną w pytaniu. Nie zawiera już frazy „non-Unicode”. Ale to tylko informacja finansowa; nie zmienia to pytania, ponieważ dotyczy to kodowań innych niż Unicode zawierających znaki, które błędnie uważano za tylko Unicode.

Solomon Rutzky
źródło
3

Pytanie zawiera błędne przekonanie o tym, czym jest Unicode. Zestaw znaków Unicode, wraz z jego kodowaniem, takim jak UTF-8 i UTF-16, jest jednym z wielu sposobów przedstawiania tekstu w komputerze, a jego celem jest zastąpienie wszystkich innych zestawów znaków i kodowań. Jeśli „dane inne niż Unicode” oznaczają „znaki nieobecne w Unicode”, to żaden tekst użyty w tej odpowiedzi nie może być zapisany w tym typie, ponieważ wszystkie litery alfabetu łacińskiego i powszechne znaki interpunkcyjne używane w codziennym języku angielskim są zawarte w Unicode.

Reprezentacje tekstowe można ogólnie rozpatrywać w dwóch częściach: zestaw znaków odwzorowujący różne znaki (litery, cyfry, symbole itp.) Na liczby na mapie odniesienia; oraz kodowanie reprezentujące te liczby jako wzorce bitów (na dysku, przez połączenie sieciowe itp.). Tutaj zajmujemy się głównie pierwszą częścią: które postacie są wymienione na listach przebojów dla określonego zestawu znaków.

Ponieważ Unicode dąży do posiadania liczb (które nazywają „punktami kodowymi”) dla każdego znaku na świecie, odniesienia takie jak Wikipedia często odnoszą się do położenia znaku w Unicode jako standardowej informacji. Nie oznacza to jednak, że inne zestawy znaków również nie mają mapowania dla tej samej postaci.

Jednym z najstarszych i najprostszych wciąż używanych zestawów znaków (i kodowania) jest ASCII, który zawiera mapowania 128 różnych znaków (od 0 do 127), ponieważ używa 7 bitów do kodowania każdego znaku. Ponieważ wyklucza to wiele znaków akcentowanych i wspólnych symboli, późniejsze kodowania wykorzystują 8 bitów i odwzorowują te same pierwsze 128 znaków, dodając do zestawu znaków poprzez wypełnienie pozycji od 128 do 255. Wśród nich godne uwagi są standardowe ISO 8859-1 i ISO 8859- 15 oraz specyficzną dla Microsoft stronę Windows Code Page 1252 .

Tak więc, aby wrócić do MS SQL Server: a „ciąg Unicode”, jak przechowywane w nchar, nvarcharlub ntextkolumnę, może reprezentować wszystkie znaki odwzorowanych w zestawie znaków Unicode, ponieważ korzysta z kodowania Unicode do przechowywania danych. A „ciąg non-Unicode”, jak przechowywane w char, varcharlub textkolumnie, może reprezentować tylko znaki odwzorowywane w innym kodowaniu . Wszystko, co możesz przechowywać w kolumnie innej niż Unicode, może być również przechowywane w kolumnie Unicode, ale nie odwrotnie.

Aby dokładnie wiedzieć, jakie znaki możesz przechowywać, musisz znać „zestawienie” w użyciu, które określa, co Microsoft określa jako „stronę kodową”, jak wyjaśniono na tej stronie odniesienia Microsoft . Prawdopodobnie w twoim przypadku używasz bardzo popularnego kodu Page 1252, o którym wspominałem wcześniej.

Znaki, które wymieniłeś istnieją zarówno w Unicode, jak i Code Page 1252:

  • Znak towarowy (™) pojawia się w Unicode na pozycji 8482 oraz w CP1252 na pozycji 153
  • Zarejestrowany (®), jak to się dzieje, pojawia się zarówno w Unicode, jak i CP1252 na pozycji 174
IMSoP
źródło
3
„Unicode to jeden z wielu sposobów kodowania tekstu do użytku na komputerze” - to nieprawda. Unicode to tylko zbiór znaków i symboli, w których każdy znak ma swój własny unikalny punkt kodowy, który jest tylko liczbą. Zadaniem kodowania jest następnie dopasowanie tych punktów kodowych do sekwencji bajtów. UTF-8 i UTF-16 są kodowaniami, Unicode nie.
szturcha
@poke W dalszej części odpowiedzi używam „kodowania” do reprezentowania zarówno „mapowania znaków do pozycji na wykresie”, jak i „reprezentacji tych pozycji jako sekwencji bitów”. Może jest lepszy termin, ale nie jestem pewien, co by to było.
IMSoP,
3
Cóż, nie możesz po prostu użyć „kodowania” z własną definicją. Przykro mi, że tu nie rozumiem, ale nie można tego zrobić w odpowiedzi, która otwiera się słowami „pytanie zawiera centralne błędne przekonanie o tym, czym jest Unicode” .
poke
2
IMSoP (i @poke): Całkowicie zgadzam się z poke w kwestii przesadnego używania słowa „kodowanie” w znaczeniu innego niż kodowanie, chociaż sympatyzuję również z dylematem IMSoP. Wolę określać Unicode jako zestaw znaków, który ma wiele kodowań, podczas gdy zazwyczaj zestaw znaków i kodowanie są używane zamiennie, ponieważ przez większość czasu (a może przez cały czas) jest to relacja 1: 1.
Solomon Rutzky