Чому тип даних varchar допускає значення unicode?


17

У мене є таблиця з стовпчиком вархара. Це дозволяє торговельну марку (™), авторські права (©) та інші символи Unicode, як показано нижче.

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 рядкові дані. Але символи торгової марки (™) та зареєстровані (®) є символами Unicode . Чи суперечить цьому визначенню властивість типу даних варшарів? Я прочитав пару посилань, як перше, так і друге . Але все ж я не міг зрозуміти, чому це дозволяє рядок unicode, коли визначення визначає, що воно дозволяє лише не-unicode рядкові значення.


12
Усі символи - це символи Unicode.
Мартін Сміт

Microsoft часто використовує UNICODE, коли вони означають UTF-16 / UCS-2. Тому вони можуть навіть не рахувати UTF-8, оскільки UNICODE - це якийсь контекст.
CodesInChaos

1
@CodesInChaos: Я намагався проаналізувати ваш коментар, але я переживаю, що ви плутаєте Unicode з різними кодовами UTF-n.
Гонки легкості з Монікою

1
@Martin Smith: Якщо всі символи є символами Unicode, то чому саме визначення microsoft varchar говорить про те, що воно дозволяє не рядкові дані Unicode?
Шива

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 .

Тут я показую вам реальний символ unicode 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)

1
Але в статті, яку ви згадали, немає жодного слова "ASCII", вони говорять лише про символи unicode та unicodode, а товарний знак (™), який ви використовували, не був unicode.
sepupic

16
"Розширений ASCII" - жахливо неоднозначний термін. Було б корисніше подивитись, що 8-бітове кодування насправді використовується (чи базується воно на налаштуваннях локальної / співставлення?). Я здогадуюсь, кодова сторінка Windows 1252 , яка дійсно кодує ™ як символ 153.
IMSoP

2
@sepupic Я думаю, вам потрібно прочитати більше про різницю між кодовими точками та кодуваннями. Вікіпедія може допомогти. "Кодування відображає (можливо, підмножину) діапазон коду Unicode вказує на послідовності значень у деякому діапазоні фіксованого розміру, що називається значеннями коду ." 8482 - це кодова точка для ™, яка може бути закодована як \ x99 (153) в Windows-1252, як \ xAA в MacRoman, як \ xE2 \ x84 \ xA2 в UTF-8 і т. Д.
допитливі

7
Слід бути обережними з 8-бітовими символами вище 127: те, що кожен код вище 127, може і може змінюватися залежно від кодування, яке використовується, яке буде змінюватися залежно від того, яке зіставлення використовується. У кодовій сторінці 1252 unicode 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

З коментарів я погоджуюсь, що "Extended ASCII" - це дійсно поганий термін, який насправді означає кодову сторінку, яка відображає символи / кодові точки в діапазоні 128-255, виходячи за стандартний діапазон коду 0-127, визначений ASCII.

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

Символ '™' може зберігатися у колонках 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 рядкові дані . Але товарний знак (™) та зареєстрований (®) символи Unicode символи . Чи суперечить цьому визначенню властивість типу даних варшарів?

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

Типи Unicode зберігають одне з декількох 8-бітових кодувань, тоді як типи Unicode зберігають єдине 16-бітове кодування Unicode: UTF-16 Little Endian. Як було зазначено в інших відповідях, які символи можуть бути збережені у 8-бітному / не-Unicode кодуванні, залежить від кодової сторінки, яка визначається зіставленням. У той час як інші зазначають, що значення байта "символу" може змінюватися в різних кодових сторінках, на яких він знаходиться, але байтове значення може навіть змінюватися в межах однієї кодової сторінки при роботі з однією з декількох сторінок коду EBCDIC (варіації Windows- 1252), які можна знайти лише в старих версіях, насправді не слід використовувати SQL Server Collations (тобто ті, у кого імена починаються з SQL_).

Отже, визначення є точним: будь-які символи, якими ви можете керувати, зберігати тип не Unicode, завжди є 8-бітним (навіть якщо вони використовують два 8-бітні значення в поєднанні як один "символ", що є тим, що Double- Набір символів байт / кодові сторінки 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 (Unicode)

Тільки 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, на яку посилається питання. Він більше не містить фразу "non-Unicode". Але це просто вигадка; це не змінює питання, оскільки мова йде про кодування Unicode, що містять символи, які помилково вважалися лише Unicode.


3

Питання містить центральне хибне уявлення про те, що таке Unicode. Набір символів Unicode разом із кодуваннями, такими як UTF-8 та UTF-16, є одним із багатьох способів подання тексту на комп’ютері, і тим, мета якого - замінити всі інші набори символів та кодування. Якщо "дані, що не стосуються Unicode" означали "символи, відсутні в Unicode", жоден текст, який я використав у цій відповіді, не міг би зберігатися в цьому типі, тому що всі букви латинського алфавіту та загальні розділові знаки, що використовуються в повсякденній англійській мові, включено в Unicode.

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

Оскільки 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стовпці, може представляти тільки символи , зображені в який - або іншому кодуванні . Все, що ви можете зберігати у стовпчику Unicode, також може зберігатися у стовпці Unicode, але не навпаки.

Щоб точно знати, які символи ви можете зберігати, вам потрібно знати "порядок використання", який диктує те, що Microsoft називає "кодовою сторінкою", як пояснено на цій довідковій сторінці Microsoft . Можливо, у вашому випадку ви використовуєте дуже поширений Кодекс, про який я згадував раніше.

Згадані вами символи існують і в Unicode, і в коді сторінки 1252:

  • Торгова марка (™) з'являється в Unicode в позиції 8482, а в CP1252 - у позиції 153
  • Зареєстрований (®), як це відбувається, з'являється в Unicode та CP1252 у позиції 174

3
"Unicode - це один із багатьох способів кодування тексту для використання на комп'ютері" - Це невірно. Unicode - це лише сукупність символів та символів, де кожен символ має свою унікальну кодову точку, яка є лише числом. Завдання кодування полягає в тому, щоб зіставити ці кодові точки з послідовністю байтів. UTF-8 і UTF-16 - це кодування, Unicode - ні.
ткнути

@poke Коли я продовжую говорити у відповіді, я використовую тут «кодування», щоб представити як «відображення символів до позицій на діаграмі», так і «представлення цих позицій як послідовності бітів». Можливо, є кращий термін для використання, але я не впевнений, що це було б.
IMSoP

3
Ну, ви не можете просто використовувати «кодування» за власним визначенням. Вибачте, що тут наштовхуєтесь, але ви не можете цього зробити у відповіді, що відкривається "питання містить центральне неправильне уявлення про те, що таке Unicode" .
ткнути

2
IMSoP (і @poke): Я повністю згоден з poke щодо надмірності щодо використання "кодування" для того, щоб означати щось інше, ніж кодування, хоча я також симпатизую дилемі IMSoP. Моя перевага - позначати Unicode як набір символів, що має декілька кодувань, тоді як зазвичай набір символів та кодування використовуються взаємозамінно через те, що більшість (або, можливо, все?) Часу є відношенням 1 на 1.
Соломон Руцький

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.