@gbn вже пояснив основну причину та виправити, але конкретна причина поведінки, яку ви бачите, така:
- Ви використовуєте
VARCHAR
літерал (без N
префікса) замість NVARCHAR
літералу (рядок з N
префіксом), отже, символ Unicode буде перетворений в VARCHAR
.
VARCHAR
це 8-бітове кодування, яке в більшості випадків - один байт на символ, але також може бути два байти на символ. З іншого боку, NVARCHAR
це 16-бітове кодування (UTF-16 Little Endian), яке є або двома байтами, або чотирма байтами на символ.
- Через різницю в кількості доступних байтів, які слід використовувати для відображення символів, 8-бітові кодування за своєю суттю значно обмежені в кількості символів, які можна відобразити.
VARCHAR
для однобайтових наборів символів - до 256 символів (більшість з них) і до 65 536 символів для двобайтових наборів символів (лише деякі з них). З іншого боку, NVARCHAR
дані можуть зіставити трохи більше 1,1 мільйона символів Unicode (хоча трохи менше 250 000).
- Через обмежену кількість відображень, які можна виконати за допомогою 8-бітових /
VARCHAR
даних, різні групи символів (на основі мови / культури) розкидаються по декількох "сторінках коду" (тобто наборах символів)
- Кожне зіставлення визначає, яку кодову сторінку, якщо вона є, використовувати для
VARCHAR
даних ( NVARCHAR
це всі символи)
- При перетворенні рядкового буквеного або змінної з
NVARCHAR
(тобто Unicode / UTF-16 / всі символи) в VARCHAR
(набір символів на основі кодової сторінки, яка вказана в більшості зібрань), використовується за замовчуванням зіставлення бази даних
- Якщо сторінка коду зіставлення, що використовується для перетворення, не містить того самого символу, але містить відображення "найкращого підходу", то буде використано відображення "найкращого пристосування".
- Якщо сторінка коду зіставлення, що використовується для перетворення, не містить того самого символу або містить відображення "найкращого підходу", тоді використовується символ "заміна" за замовчуванням (найчастіше
?
).
Отже, що ви бачите , є NVARCHAR
для VARCHAR
перетворення з - за відсутності в N
приставку на строковий літерал. І, Кодова сторінка за замовчуванням Збір за базою даних не містить точно такого ж символу, але було знайдено "найкраще" відображення, саме тому ви отримуєте 2
замість ?
.
Ви можете побачити цей ефект, виконавши такий простий тест:
SELECT '₂', N'₂';
Повернення:
2 ₂
Щоб було зрозуміло, якби кодова сторінка зібраного за замовчуванням зіставлення баз даних містила абсолютно той самий символ, то вона була б переведена на той самий символ на цій кодовій сторінці. І, тоді, у вашому випадку, оскільки ви зберігаєте в NVARCHAR
стовпчик, він переклав би знову, до початкового символу Unicode. Останній приклад нижче показує таку поведінку.
ВАЖЛИВО: Будь ласка, майте на увазі, що перетворення відбувається під час інтерпретації рядкового літералу, який є перед його збереженням у стовпчик. Це означає, що навіть якщо стовпець може містити цей символ, він уже буде перетворений на щось інше, грунтуючись на зіставленні бази даних за замовчуванням, все через відмову від N
префікса у цьому рядковому літералі. І саме це ви переживаєте (або були).
Наприклад, якщо зіставлення вашої бази даних за замовчуванням було б одним із корейських зібрань (один із чотирьох наборів символів з двома байтами), ви б не бачили цієї проблеми, оскільки в цьому символі доступний символ "Підписка 2". набір (Код, сторінка 949). Спробуйте переконатися в наступному тесті (він використовує зіставлення стовпця замість зіставлення бази даних за замовчуванням, як це простіше показати):
CREATE TABLE #TestChar
(
[8bit_Latin1_General-1252] VARCHAR(2) COLLATE Latin1_General_100_CI_AS_SC,
[8bit_Korean-949] VARCHAR(2) COLLATE Korean_100_CI_AS_SC,
[UTF16LE_Latin1_General-1252] NVARCHAR(2) COLLATE Latin1_General_100_CI_AS_SC
);
INSERT INTO #TestChar VALUES (N'₂', N'₂', N'₂');
SELECT * FROM #TestChar;
Повернення:
8bit_Latin1_General-1252 8bit_Korean-949 UTF16LE_Latin1_General-1252
2 ₂ ₂
Як бачимо, Latin1_General Collations, які використовують код даних для сторінки даних (Кодова сторінка Modern_Spanish
) VARCHAR
, не відповідають точній відповідності, але вони мають "найкраще" відображення (що саме ви бачите ). АЛЕ, корейські зібрання, які використовують код VARCHAR
даних для даних, мають точну відповідність символу "Підписка 2".
Для подальшої ілюстрації ми можемо створити нову Базу даних із зіставленням за замовчуванням одного з корейських зібрань, а потім запустити точний SQL, про який йдеться.
CREATE DATABASE [TestKorean-949] COLLATE Korean_100_CI_AS_KS_WS_SC;
ALTER DATABASE [TestKorean-949] SET RECOVERY SIMPLE;
GO
USE [TestKorean-949];
CREATE TABLE test (
id INT NOT NULL,
description NVARCHAR(100) COLLATE Modern_Spanish_CI_AS NOT NULL
);
INSERT INTO test (id, description) VALUES (1, 'CO2');
SELECT * FROM test WHERE id = 1;
UPDATE test SET description = 'CO₂' WHERE id = 1;
SELECT * FROM test WHERE id = 1;
Повернення:
id description
1 CO2
id description
1 CO₂
ОНОВЛЕННЯ
Для всіх, хто зацікавлений у тому, щоб дізнатись більше про те , що саме тут відбувається (тобто про всі деталі горі), будь ласка, дивіться розслідування, яке я щойно розмістив у двох частинах