@gbn đã giải thích lý do cơ bản và cách khắc phục, nhưng lý do cụ thể cho hành vi mà bạn đang thấy là đây:
- Bạn đang sử dụng một
VARCHAR
chữ (không có N
tiền tố) thay vì bằng NVARCHAR
chữ (chuỗi có N
tiền tố), do đó, ký tự Unicode sẽ được chuyển đổi thành VARCHAR
.
VARCHAR
là một mã hóa 8 bit, trong hầu hết các trường hợp, một byte cho mỗi ký tự, nhưng cũng có thể là hai byte cho mỗi ký tự. Mặt khác, NVARCHAR
mã hóa 16 bit (UTF-16 Little Endian) là hai byte hoặc bốn byte cho mỗi ký tự.
- Do sự khác biệt về số lượng byte có sẵn để sử dụng để ánh xạ các ký tự, mã hóa 8 bit, về bản chất, bị hạn chế hơn nhiều về số lượng ký tự có thể được ánh xạ.
VARCHAR
dữ liệu lên tới 256 ký tự cho Bộ ký tự một byte (phần lớn trong số chúng) và tối đa 65.536 ký tự cho Bộ ký tự Double-Byte (chỉ một vài trong số này). Mặt khác, NVARCHAR
dữ liệu có thể ánh xạ chỉ hơn 1,1 triệu ký tự Unicode (mặc dù chỉ dưới 250k hiện được ánh xạ).
- Do số lượng ánh xạ có thể được thực hiện với 8 bit /
VARCHAR
dữ liệu, các nhóm ký tự khác nhau (dựa trên Ngôn ngữ / Văn hóa) được trải rộng trên nhiều "Trang mã" (nghĩa là bộ ký tự)
- Mỗi Collation chỉ định Trang mã nào, nếu có, được sử dụng cho
VARCHAR
dữ liệu ( NVARCHAR
là tất cả các ký tự)
- Khi chuyển đổi một chuỗi bằng chữ hoặc biến từ
NVARCHAR
(ví dụ Unicode / UTF-16 / tất cả các ký tự) thành VARCHAR
(bộ ký tự dựa trên Trang mã được chỉ định trong hầu hết các Collations), Collation mặc định của Cơ sở dữ liệu được sử dụng
- Nếu Trang Mã của Đối chiếu được sử dụng cho chuyển đổi không chứa cùng một ký tự, nhưng chứa ánh xạ "phù hợp nhất", thì ánh xạ "phù hợp nhất" sẽ được sử dụng.
- Nếu Trang Mã của Đối chiếu đang được sử dụng cho chuyển đổi không chứa cùng một ký tự hoặc chứa ánh xạ "phù hợp nhất", thì ký tự "thay thế" mặc định sẽ được sử dụng (phổ biến nhất
?
).
Vì vậy, những gì bạn đang nhìn thấy là một NVARCHAR
để VARCHAR
chuyển đổi do thiếu N
tiền tố đen trên chuỗi. Và, Trang Mã của Đối chiếu mặc định cho Cơ sở dữ liệu không chứa chính xác cùng một ký tự, nhưng đã tìm thấy ánh xạ "phù hợp nhất", đó là lý do tại sao bạn nhận được 2
thay vì?
.
Bạn có thể thấy hiệu ứng này bằng cách thực hiện bài kiểm tra đơn giản sau:
SELECT '₂', N'₂';
Trả về:
2 ₂
Để rõ ràng, NẾU Trang Mã của Đối chiếu mặc định cho Cơ sở dữ liệu có chứa cùng một ký tự, thì nó sẽ được dịch sang cùng một ký tự trong Trang Mã đó. Và, sau đó, trong trường hợp của bạn, vì bạn đang lưu trữ vào một NVARCHAR
cột, nên nó sẽ được dịch lại, trở lại ký tự Unicode gốc. Ví dụ cuối cùng dưới đây cho thấy hành vi này.
QUAN TRỌNG: Xin lưu ý rằng việc chuyển đổi xảy ra khi chuỗi ký tự đang được diễn giải, đó là trước khi nó được lưu trữ vào cột. Điều này có nghĩa là ngay cả khi cột có thể giữ ký tự đó, thì nó cũng đã được chuyển đổi thành thứ khác, dựa trên Collation mặc định của Cơ sở dữ liệu, tất cả là do bỏ điN
tiền tố trên chuỗi ký tự đó. Và đây chính xác là những gì bạn đang (hoặc đã) trải qua.
Ví dụ: nếu Collation mặc định của Cơ sở dữ liệu của bạn là một trong các Collation Hàn Quốc (một trong bốn Bộ ký tự Double-Byte), thì bạn sẽ không gặp vấn đề này vì ký tự "Đăng ký 2" có sẵn trong ký tự đó đặt (Mã trang 949). Hãy thử kiểm tra sau để xem (nó sử dụng Collation của cột thay vì Collation mặc định của Cơ sở dữ liệu vì dễ hiển thị hơn):
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;
Trả về:
8bit_Latin1_General-1252 8bit_Korean-949 UTF16LE_Latin1_General-1252
2 ₂ ₂
Như bạn có thể thấy, Bộ sưu tập Latin1_General, sử dụng Mã trang 1252 (cùng Trang mã mà Bộ Modern_Spanish
sưu tập sử dụng) cho VARCHAR
dữ liệu, không có kết quả khớp chính xác, nhưng chúng có ánh xạ "phù hợp nhất" (đó là những gì bạn đang thấy ). NHƯNG, Bộ sưu tập Hàn Quốc, sử dụng VARCHAR
dữ liệu Trang 949 cho dữ liệu, có một kết quả khớp chính xác cho ký tự "Đăng ký 2".
Để minh họa thêm, chúng ta có thể tạo Cơ sở dữ liệu mới với Collation mặc định của một trong các Collations của Hàn Quốc, sau đó chạy SQL chính xác có trong câu hỏi:
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;
Trả về:
id description
1 CO2
id description
1 CO₂
CẬP NHẬT
Đối với bất kỳ ai quan tâm đến việc tìm hiểu thêm về chính xác những gì đang diễn ra ở đây (tức là tất cả các thông tin chi tiết), vui lòng xem cuộc điều tra hai phần tôi vừa đăng: