Почему тип данных varchar допускает значения Юникода?

17

У меня есть таблица с колонной Varchar. Это позволяет использовать товарный знак (™), авторские права (©) и другие символы Юникода, как показано ниже.

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

Но определение varchar гласит, что он допускает строковые данные не в Юникоде. Но торговые марки (™) и зарегистрированные (®) символы являются символами Unicode . Противоречит ли определение свойству типа данных varchar? Я прочитал пару ссылок, как первая и вторая . Но все же я не мог понять, почему он допускает строку в кодировке Юникод, когда в определении говорится, что он допускает только значения строк, отличные от Юникод.

Шива
источник
12
Все символы являются символами Юникода.
Мартин Смит,
Microsoft часто использует UNICODE, когда они имеют в виду UTF-16 / UCS-2. Таким образом, они могут даже не считать UTF-8, поскольку UNICODE является некоторым контекстом.
CodesInChaos
1
@CodesInChaos: я изо всех сил пытался разобрать ваш комментарий, но я беспокоюсь, что вы путаете Юникод с различными кодировками UTF-n.
Легкость гонок с Моникой
1
@ Мартин Смит: Если все символы являются символами Юникода, то почему определение Microsoft Varchar говорит, что оно допускает строковые данные не в Юникоде?
Шива
2
кодировка символов в varchar не является Unicode, но все символы существуют в Unicode
Мартин Смит,

Ответы:

15

Но торговые марки (™) и зарегистрированные (®) символы являются символами Unicode.

Вы не правы здесь. Ваши строки содержат только asciiсимволы.

Вот простой тест, который показывает, что все ваши символы - ascii (+ некоторые extended asciiс кодами ascii между 128 и 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;

Здесь вы можете ясно видеть, что все ваши символы закодированы в 1 байт:

введите описание изображения здесь

Да, они не являются чистыми символами ASCII, но они являются расширенными ASCII .

Здесь я покажу вам настоящий символ Юникода, Trademark(™)его код и двоичное представление:

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;

введите описание изображения здесь

Наконец, вы можете видеть, что Trademark(™)символ Unicode имеет код 8482, а не 153:

select nchar(8482), nchar(153)
sepupic
источник
1
Но в упомянутой статье нет слова «ASCII», они говорят только о Unicode и не-Unicode символах, а используемый вами товарный знак (™) не был Unicode.
сентября
16
«Extended ASCII» - ужасно неоднозначный термин. Было бы более полезно взглянуть на то, что на самом деле используется 8-битное кодирование (основано ли оно на настройках локали / параметров сортировки?). Я предполагаю кодовую страницу Windows 1252 , которая действительно кодирует ™ как символ 153.
IMSoP
2
@sepupic Я думаю, вам нужно больше узнать о разнице между кодовыми точками и кодировками. Википедия может помочь. «Кодирование отображает (возможно, подмножество) диапазон кодов Unicode указывает на последовательности значений в некотором диапазоне фиксированного размера, называемых кодовыми значениями ». 8482 - это кодовая точка для ™, которая может быть закодирована как \ x99 (153) в Windows-1252, как \ xAA в MacRoman, как \ xE2 \ x84 \ xA2 в UTF-8 и т. Д.
curiousdannii
7
Следует соблюдать осторожность с 8-разрядными символами выше 127: то, что представляет каждый код выше 127, может и будет меняться в зависимости от используемой кодировки, которая будет варьироваться в зависимости от того, какая сортировка используется. В кодовой странице 1252 юникод 8482 отображается на 153. В кодовой странице 850 это место занимает 214 ( Ö), а в ISO-8859-1 (иногда называемом Latin1) это контрольный код без печатаемого представления. Если вы не знаете, что всегда будете использовать одну и ту же кодовую страницу, безопаснее придерживаться символов ANSI (127 или менее) или использовать типы Unicode. Кодовая страница 1252 наиболее распространена в SQL Server, но далеко не повсеместна.
Дэвид Спиллетт
4
@Shiva Абсолютный минимум Каждый разработчик программного обеспечения Абсолютно, положительно должен знать о Unicode и наборах символов . ASCII является подмножеством многих кодировок, и почти все эти кодировки содержат не-ASCII символы и одновременно не являются Unicode. И Unicode также имеет много разных кодировок (таких как UTF-8, UTF-32 и т. Д.).
jpmc26
7

Из комментариев я согласен, что «расширенный ASCII» - это действительно плохой термин, который фактически означает кодовую страницу, которая отображает символы / кодовые точки в диапазоне 128–255, за пределы стандартного диапазона 0–127 кодовых точек, определенного ASCII.

SQL Server поддерживает множество кодовых страниц с помощью сопоставлений. Символы, не входящие в ASCII, могут храниться в varchar до тех пор, пока базовое сопоставление поддерживает этот символ.

Символ '™' может храниться в столбцах varchar / char, если кодовая страница сопоставления SQL Server 1250 или более. Ниже приведен запрос:

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

Но только их подмножество также поддерживает символ '©', поэтому для поддержки обоих параметров сортировки столбцов должно быть одно из следующих:

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;
Дэн Гусман
источник
4

Но определение varchar гласит, что он допускает строковые данные не в Юникоде . Но товарный знак (™) и зарегистрированный (®) символы Unicode символы . Противоречит ли определение свойству типа данных varchar?

Хотя другие ответы не являются правильными, я думаю, что это поможет указать на путаницу в базовой терминологии. Я подчеркнул два слова в приведенной выше цитате из вопроса в качестве примера этой путаницы. Когда документация по SQL Server говорит о Unicode и не-Unicode данных , они не говорят о символах . Они говорят о последовательности байтов, которые представляют определенные символы. Основное различие между типами Unicode ( NCHAR, NVARCHAR, XML, и устаревшим / злой NTEXT) и типами не-Unicode ( CHAR, VARCHARи устаревший / злом TEXT) является то , что типы из последовательности байт они могут хранить.

Типы, отличные от Unicode, хранят одну из нескольких 8-битных кодировок, а типы Unicode хранят одну 16-битную кодировку Unicode: UTF-16 Little Endian. Как уже упоминалось в других ответах, какие символы могут быть сохранены в 8-битной кодировке / кодировке, не относящейся к Юникоду, зависит от кодовой страницы, которая определяется с помощью сортировки. В то время как другие отметили, что значение байта «символа» может варьироваться в зависимости от кодовых страниц, на которых он обнаружен, значение байта может даже варьироваться в пределах одной и той же кодовой страницы при работе с одной из нескольких кодовых страниц EBCDIC (разновидности Windows- 1252), которые можно найти только в более старых версиях, которые не должны использоваться в действительности в SQL Server Collations (то есть, имена, начинающиеся с SQL_).

Следовательно, определение является точным: любые символы, которые вы можете сохранить в не-Unicode-типе, всегда являются 8-битными (даже если они используют два 8-битных значения в комбинации как один «символ», что является Набор байтовых символов / кодовые страницы DBCS позволяют). И типы данных Unicode всегда 16-битные, даже если они иногда используют два 16-битных значения в комбинации как один «символ» (т. Е. Суррогатная пара, которая, в свою очередь, представляет дополнительный символ).

И, поскольку SQL Server изначально поддерживает кодировку UTF-8 VARCHARи CHARтипы данных с SQL Server 2019,

VARCHARбольше не может называться «не-Unicode». Итак, начиная с первой общедоступной бета-версии SQL Server 2019 в сентябре 2018 года, мы должны называть VARCHARего «8-битным типом данных», даже если речь идет о версиях, предшествующих SQL Server 2019. Эта терминология верна для всех 4 типов кодировок, которые можно использовать с VARCHAR:

  1. Расширенный ASCII
  2. Двухбайтовые наборы символов (DBCS)
  3. EBCDIC
  4. UTF-8 (Юникод)

Только TEXTтип данных (устарел начиная с SQL Server 2005, поэтому не используйте его) является «не-Unicode», но это лишь техническая составляющая, и ссылка на него как «8-битный тип данных» является точной.

NVARCHAR, NCHARИ NTEXTмогут быть отнесены к «UTF-16» или «16-битового типа данных». Я полагаю, что Oracle использует терминологию «только для Unicode» NVARCHAR, но это не исключает возможности использования UTF-8 (также кодировки Unicode), который не будет работать, поэтому, вероятно, лучше придерживаться первые два варианта.

Подробнее о новых кодировках UTF-8 читайте в моем сообщении:

Собственная поддержка UTF-8 в SQL Server 2019: Спаситель или Лжепророк?

PS Я медленно работаю над обновлением документации по SQL Server, чтобы отразить эти изменения.

PPS Microsoft уже обновила некоторые страницы с информацией UTF-8, включая документацию по char и varchar, упомянутую в этом вопросе. Он больше не содержит фразу "не-Unicode". Но это только к вашему сведению; это не меняет вопроса, поскольку речь идет о кодировках не в Юникоде, содержащих символы, которые по ошибке считались единственными в Юникоде.

Соломон Руцкий
источник
3

Вопрос содержит центральное неправильное представление о том, что такое Юникод. Набор символов Unicode, наряду с его кодировками, такими как UTF-8 и UTF-16, является одним из многих способов представления текста в компьютере, и его целью является замена всех других наборов символов и кодировок. Если «данные не в Юникоде» означают «символы, отсутствующие в Юникоде», то ни один из текстов, которые я использовал в этом ответе, не может быть сохранен в этом типе, потому что все буквы латинского алфавита и обычные знаки препинания, используемые в повседневном английском языке, включен в Юникод.

Текстовые представления можно широко представить в двух частях: набор символов, отображающий различные символы (буквы, цифры, символы и т. Д.) На числа в справочной таблице; и кодирование, представляющее эти числа в виде шаблонов битов (на диске, по сетевому соединению и т. д.). Здесь нас больше всего интересует первая часть: какие символы перечислены на диаграммах для определенного набора символов.

Так как Unicode стремится иметь числа (которые он называет «кодовыми точками») для каждого символа в мире, ссылки, такие как Википедия, часто будут ссылаться на позицию Unicode символа как стандартную часть справочной информации. Однако это не означает, что другие наборы символов также не имеют сопоставления для того же символа.

Один из самых старых и самых простых наборов символов (и кодировок), все еще используемых, является ASCII, который имеет отображения для 128 различных символов (от 0 до 127), потому что он использует 7 битов для кодирования каждого символа. Поскольку это исключает множество акцентированных символов и общих символов, более поздние кодировки используют 8 битов и отображают те же самые первые 128 символов, добавляя к набору символов, заполняя позиции от 128 до 255. Среди них следует отметить стандартные ISO 8859-1 и ISO 8859- 15 , и специфическая для Microsoft кодовая страница Windows 1252 .

Таким образом, чтобы вернуться к MS SQL Server: «строка Unicode», как хранится в одном nchar, nvarcharили ntextстолбце, может представлять все символы , отображенные в наборе символов Unicode, поскольку он использует Unicode , кодирующим для хранения данных. А «строка не-Unicode», которые хранятся в char, varcharили textстолбце, может представлять только символы , отображенные в какой - либо другой кодировке . Все, что вы можете сохранить в столбце не в Юникоде, также может храниться в столбце в Юникоде, но не наоборот.

Чтобы точно знать, какие символы вы можете хранить, вам нужно знать используемое «сопоставление», которое диктует то, что Microsoft называет «кодовой страницей», как описано на этой справочной странице Microsoft . Вероятно, в вашем случае вы используете очень распространенную кодовую страницу 1252, о которой я упоминал ранее.

Упомянутые вами символы существуют как в Юникоде, так и в Кодовой странице 1252:

  • Торговая марка (™) появляется в Unicode в позиции 8482 и в CP1252 в позиции 153
  • Зарегистрированный (®), как это происходит, появляется как в Юникоде, так и в CP1252 в позиции 174
IMSoP
источник
3
«Юникод - это один из многих способов кодирования текста для использования на компьютере» - это не правильно. Unicode - это просто набор символов и символов, где каждый символ имеет свою уникальную кодовую точку, которая является просто числом. Задача кодирования состоит в том, чтобы сопоставить эти кодовые точки с байтовой последовательностью. UTF-8 и UTF-16 являются кодировками, а Unicode - нет.
тыкай
@poke Как я скажу далее в ответе, я использую здесь «кодирование» для представления «отображения символов на позиции на диаграмме» и «представления этих позиций в виде последовательности битов». Может быть, есть лучший термин для использования, но я не уверен, что это будет.
IMSoP
3
Ну, вы не можете просто использовать «кодирование» с вашим собственным определением. Извините, что придираюсь здесь, но вы не можете сделать это в ответе, который начинается с «вопрос содержит центральное неправильное представление о том, что такое Юникод» .
тыкай
2
IMSoP (и @poke): Я полностью согласен с poke в отношении чрезмерного использования «кодирования» для обозначения чего-то иного, чем кодирование, хотя я также сочувствую дилемме IMSoP. Я предпочитаю ссылаться на Unicode как на набор символов, который имеет несколько кодировок, в то время как обычно набор символов и кодировка используются взаимозаменяемо из-за того, что в большинстве случаев (или, может быть, все?) Соотношение 1: 1.
Соломон Руцкий