Tại sao kiểu dữ liệu varchar cho phép các giá trị unicode?


17

Tôi có một bảng với một cột varchar. Nó đang cho phép Nhãn hiệu (™), bản quyền (©) và các ký tự Unicode khác như được hiển thị bên dưới.

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

Nhưng định nghĩa của varchar nói, nó cho phép dữ liệu chuỗi không unicode. Nhưng các ký hiệu Nhãn hiệu (™) và Đã đăng ký (®) là các tự Unicode . Liệu định nghĩa có mâu thuẫn với thuộc tính của kiểu dữ liệu varchar không? Tôi đọc một vài liên kết như đầu tiênthứ hai . Nhưng tôi vẫn không thể hiểu tại sao nó cho phép chuỗi unicode khi định nghĩa nói rằng nó chỉ cho phép các giá trị chuỗi không unicode.


12
Tất cả các ký tự là ký tự Unicode.
Martin Smith

Microsoft thường sử dụng UNICODE khi chúng có nghĩa là UTF-16 / UCS-2. Vì vậy, họ thậm chí có thể không tính UTF-8 vì UNICODE là một số bối cảnh.
CodeInChaos

1
@CodesInChaos: Tôi đã đấu tranh để phân tích nhận xét của bạn, nhưng tôi lo lắng rằng bạn đang nhầm lẫn Unicode với các bảng mã UTF-n khác nhau.
Cuộc đua nhẹ nhàng với Monica

1
@Martin Smith: Nếu tất cả các ký tự là ký tự Unicode, thì tại sao định nghĩa varchar của microsoft nói rằng nó cho phép dữ liệu chuỗi không Unicode?
Shiva

2
mã hóa cho các ký tự trong varchar không phải là unicode nhưng tất cả các ký tự tồn tại trong unicode
Martin Smith

Câu trả lời:


15

Nhưng các ký hiệu Nhãn hiệu (™) và Đã đăng ký (®) là các ký tự Unicode.

Bạn đã sai ở đây. Chuỗi của bạn chỉ chứa các asciiký tự.

Dưới đây là một thử nghiệm đơn giản cho bạn thấy rằng các nhân vật của bạn đều là ascii (+ một số extended asciicó mã ascii trong khoảng từ 128 đến 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;

Ở đây bạn có thể thấy rõ rằng tất cả các ký tự của bạn được mã hóa 1 byte:

nhập mô tả hình ảnh ở đây

Vâng, chúng không phải là các ký tự ascii thuần túy nhưng chúng là Extended ASCII .

Ở đây tôi cho bạn thấy ký tự unicode thực Trademark(™)và mã đại diện và mã nhị phân của nó:

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;

nhập mô tả hình ảnh ở đây

Cuối cùng, bạn có thể thấy Trademark(™)ký tự unicode có mã 8482 chứ không phải 153:

select nchar(8482), nchar(153)

1
Nhưng không có từ "ASCII" trong bài viết mà bạn đề cập, họ đang nói về các ký tự unicode và không unicode và Nhãn hiệu (™) mà bạn đã sử dụng không phải là unicode.
nhiễm trùng

16
"ASCII mở rộng" là một thuật ngữ mơ hồ khủng khiếp. Sẽ hữu ích hơn khi xem xét mã hóa 8 bit nào thực sự được sử dụng (có dựa trên cài đặt ngôn ngữ / đối chiếu không?). Tôi đoán mã trang Windows 1252 , thực sự mã hóa ™ thành ký tự 153.
IMSoP

2
@sepupic Tôi nghĩ bạn cần đọc thêm về sự khác biệt giữa tiền mã hóa và mã hóa. Wikipedia có thể giúp đỡ. "Một bản đồ mã hóa (có thể là một tập hợp con) phạm vi mã Unicode trỏ đến chuỗi các giá trị trong một số phạm vi kích thước cố định, được gọi là các giá trị mã ." 8482 là điểm mã cho ™, có thể được mã hóa như \ x99 (153) trong Windows-1252, như \ XAA trong macroman, như \ xE2 \ x84 \ xA2 trong UTF-8 vv
curiousdannii

7
Cần thận trọng với các ký tự 8 bit trên 127: mỗi mã trên 127 đại diện cho những gì có thể và sẽ thay đổi tùy thuộc vào mã hóa được sử dụng sẽ thay đổi tùy theo việc sử dụng đối chiếu nào. Trong codepage 1252 unicode 8482 được ánh xạ tới 153. Trong codepage 850, điểm đó được lấy bởi 214 ( Ö) và trong ISO-8859-1 (đôi khi được gọi là Latin1), đó là mã điều khiển không có biểu diễn có thể in được. Trừ khi bạn biết bạn sẽ luôn sử dụng cùng một bảng mã, sẽ an toàn hơn khi sử dụng các ký tự ANSI (127 trở xuống) hoặc sử dụng các loại Unicode. Codepage 1252 là phổ biến nhất trong SQL Server nhưng không phổ biến.
David Spillett

4
@Shiva Tối thiểu tuyệt đối Mỗi nhà phát triển phần mềm Tuyệt đối, Tích cực phải biết về Unicode và các bộ ký tự . ASCII là tập hợp con của nhiều bảng mã và hầu như tất cả các bảng mã đó đều chứa các ký hiệu không phải ASCII và đồng thời không phải là Unicode. Và Unicode cũng có nhiều bảng mã khác nhau (như UTF-8, UTF-32, v.v.).
jpmc26

7

Từ các ý kiến, tôi đồng ý "ASCII mở rộng" thực sự là một thuật ngữ tồi có nghĩa là một trang mã ánh xạ các ký tự / điểm mã trong phạm vi 128-255, vượt ra ngoài phạm vi điểm mã 0-127 tiêu chuẩn được xác định bởi ASCII.

SQL Server hỗ trợ nhiều trang mã thông qua các đối chiếu. Các ký tự không phải ASCII có thể được lưu trữ trong varchar miễn là đối chiếu cơ bản hỗ trợ ký tự.

Ký tự '™' có thể được lưu trữ trong các cột varchar / char khi trang mã đối chiếu SQL Server là 1250 trở lên. Dưới đây truy vấn sẽ liệt kê những điều sau đây:

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

Nhưng chỉ một tập hợp con trong số này cũng hỗ trợ ký tự '©' nên việc đối chiếu cột sẽ cần phải là một trong những điều sau đây để hỗ trợ cả hai:

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

Nhưng định nghĩa của varchar nói, nó cho phép dữ liệu chuỗi không unicode . Nhưng các ký hiệu Nhãn hiệu (™) và Đã đăng ký (®) là các ký tự Unicode . Liệu định nghĩa có mâu thuẫn với thuộc tính của kiểu dữ liệu varchar không?

Trong khi các câu trả lời khác không phải là không chính xác, tôi nghĩ nó sẽ giúp chỉ ra một sự nhầm lẫn trong thuật ngữ cơ sở. Tôi đã nhấn mạnh hai từ trong trích dẫn trên từ câu hỏi như một ví dụ về sự nhầm lẫn này. Khi tài liệu SQL Server nói về dữ liệu Unicode và phi Unicode , họ không nói về các ký tự . Họ đang nói về các chuỗi byte đại diện cho các ký tự nhất định. Sự khác biệt chính giữa các loại Unicode ( NCHAR, NVARCHAR, XML, và bị phản đối / ác NTEXT) và các loại phi Unicode ( CHAR, VARCHARvà bị phản đối / ác TEXT) là những gì loại của chuỗi byte họ có thể lưu trữ.

Các loại không Unicode lưu trữ một trong nhiều mã hóa 8 bit, trong khi các loại Unicode lưu trữ một mã hóa Unicode 16 bit duy nhất: UTF-16 Little Endian. Như các câu trả lời khác đã đề cập, những ký tự nào có thể được lưu trữ trong mã hóa 8 bit / không Unicode tùy thuộc vào trang mã, được xác định bởi Collation. Mặc dù những người khác đã lưu ý rằng giá trị byte của "ký tự" có thể khác nhau giữa các trang mã được tìm thấy trên đó, giá trị byte thậm chí có thể thay đổi trong cùng một trang mã khi xử lý một trong một số trang mã EBCDIC (các biến thể của Windows- 1252), vốn chỉ được tìm thấy trong các Bộ sưu tập SQL Server cũ hơn, không nên sử dụng (nghĩa là những cái có tên bắt đầu bằng SQL_).

Do đó, định nghĩa là chính xác: bất kỳ ký tự nào bạn có thể quản lý để lưu trữ trong loại không phải là Unicode luôn là 8 bit (ngay cả khi chúng sử dụng hai giá trị 8 bit kết hợp thành một "ký tự" duy nhất, đó là ký tự Double- Các trang mã bộ ký tự / DBCS cho phép). Và các kiểu dữ liệu Unicode luôn luôn là 16 bit, ngay cả khi đôi khi chúng sử dụng hai giá trị 16 bit kết hợp thành một "ký tự" duy nhất (nghĩa là một cặp thay thế đại diện cho một ký tự bổ sung).

VÀ, do SQL Server thực sự hỗ trợ mã hóa UTF-8 cho VARCHARCHARkiểu dữ liệu kể từ SQL Server 2019,

VARCHARkhông còn có thể được gọi là "không Unicode". Vì vậy, bắt đầu với phiên bản beta công khai đầu tiên của SQL Server 2019 vào tháng 9 năm 2018, chúng ta nên gọi VARCHARlà "kiểu dữ liệu 8 bit", ngay cả khi nói về các phiên bản trước SQL Server 2019. Thuật ngữ này đúng với cả 4 loại mã hóa có thể được sử dụng với VARCHAR:

  1. ASCII mở rộng
  2. Bộ ký tự nhân đôi (DBCS)
  3. EBCDIC
  4. UTF-8 (Unicode)

Chỉ có TEXTkiểu dữ liệu (không được dùng trong SQL Server 2005, vì vậy không sử dụng nó) là "không phải là Unicode", nhưng đó chỉ là một kỹ thuật và gọi nó là "kiểu dữ liệu 8 bit" là chính xác.

NVARCHAR, NCHARNTEXTcó thể được gọi là "UTF-16" hoặc "kiểu dữ liệu 16 bit". Oracle, tôi tin rằng, sử dụng thuật ngữ "chỉ dành cho Unicode" NVARCHAR, nhưng điều đó không loại trừ rõ ràng khả năng sử dụng UTF-8 (cũng là mã hóa Unicode), vì vậy có lẽ tốt nhất để sử dụng hai lựa chọn đầu tiên.

Để biết chi tiết về mã hóa UTF-8 mới, vui lòng xem bài viết của tôi:

Hỗ trợ UTF-8 bản địa trong SQL Server 2019: Tiên tri cứu rỗi hay sai?

PS Tôi đang dần dần tìm cách cập nhật tài liệu SQL Server để phản ánh những thay đổi này.

PPS Microsoft đã cập nhật một số trang với thông tin UTF-8, bao gồm tài liệu char và varchar được tham chiếu trong câu hỏi. Nó không còn chứa cụm từ "không Unicode". Nhưng đó chỉ là một FYI; nó không thay đổi câu hỏi vì đây là về mã hóa phi Unicode chứa các ký tự bị nhầm tưởng là chỉ Unicode.


3

Câu hỏi chứa một quan niệm sai lầm trung tâm về Unicode là gì. Bộ ký tự Unicode, cùng với các bảng mã của nó như UTF-8 và UTF-16, là một trong nhiều cách biểu diễn văn bản trong máy tính, và một mục tiêu là thay thế tất cả các bộ ký tự và mã hóa khác. Nếu "dữ liệu không phải là Unicode" có nghĩa là "các ký tự không có trong Unicode", thì không có văn bản nào tôi sử dụng trong câu trả lời này có thể được lưu trữ trong loại đó, bởi vì tất cả các chữ cái trong bảng chữ cái Latinh và dấu câu phổ biến được sử dụng trong tiếng Anh hàng ngày là bao gồm trong Unicode.

Các biểu diễn văn bản có thể được suy nghĩ rộng rãi thành hai phần: một bộ ký tự ánh xạ các ký tự khác nhau (chữ cái, chữ số, ký hiệu, v.v.) thành các số trên biểu đồ tham chiếu; và một mã hóa đại diện cho các số đó dưới dạng các mẫu của bit (trên đĩa, qua kết nối mạng, v.v.). Ở đây chúng tôi chủ yếu quan tâm đến phần đầu tiên: những nhân vật được liệt kê trên bảng xếp hạng cho một bộ ký tự cụ thể.

Vì Unicode nhằm mục đích có các số (mà nó gọi là "điểm mã") cho mọi ký tự trên thế giới, các tài liệu tham khảo như Wikipedia thường sẽ đề cập đến vị trí Unicode của một ký tự như một thông tin tham chiếu tiêu chuẩn. Tuy nhiên, điều đó không có nghĩa là các bộ ký tự khác cũng không có ánh xạ cho cùng một ký tự đó.

Một trong những bộ ký tự lâu đời nhất và đơn giản nhất (và mã hóa) vẫn được sử dụng là ASCII, có ánh xạ cho 128 ký tự khác nhau (0 đến 127), vì nó sử dụng 7 bit để mã hóa mỗi ký tự. Do loại trừ nhiều ký tự có dấu và ký hiệu phổ biến, các bảng mã sau này sử dụng 8 bit và ánh xạ 128 ký tự đầu tiên, thêm vào bộ ký tự bằng cách điền vào các vị trí 128 đến 255. Đáng chú ý trong số này là ISO 8859-1ISO 8859- 15Mã Windows cụ thể của Microsoft Trang 1252 .

Vì vậy, để trở về với MS SQL Server: một "Unicode string", như lưu trữ trong một nchar, nvarcharhoặc ntextcột, có thể đại diện cho tất cả các nhân vật ánh xạ trong bộ ký tự Unicode, vì nó sử dụng một Unicode mã hóa để lưu trữ dữ liệu. Một "non-Unicode chuỗi", như lưu trữ trong một char, varcharhoặc textcột, có thể đại diện cho những chữ số ánh xạ trong một số mã hóa khác . Bất cứ điều gì bạn có thể lưu trữ trong một cột không Unicode cũng có thể được lưu trữ trong một cột Unicode, nhưng không phải ngược lại.

Để biết chính xác những ký tự nào bạn có thể lưu trữ, bạn cần biết "đối chiếu" đang sử dụng, điều này chỉ ra những gì Microsoft gọi là "trang mã", như được giải thích trên trang tham khảo Microsoft này . Trong trường hợp của bạn, có thể bạn đang sử dụng Bộ luật rất phổ biến, mà tôi đã đề cập trước đó.

Các ký tự bạn đã đề cập tồn tại trong cả Unicode và Mã Trang 1252:

  • Thương hiệu (™) xuất hiện bằng Unicode ở vị trí 8482 và ở CP1252 ở vị trí 153
  • Đã đăng ký (®), như đã xảy ra, xuất hiện trong cả Unicode và CP1252 ở vị trí 174

3
Unicode Unicode là một trong nhiều cách mã hóa văn bản để sử dụng trong máy tính . Điều đó không đúng. Unicode chỉ là một tập hợp các ký tự và ký hiệu, trong đó mỗi ký tự có điểm mã duy nhất của riêng nó chỉ là một số. Công việc mã hóa sau đó là khớp các điểm mã đó với một chuỗi byte. UTF-8 và UTF-16 là mã hóa, Unicode thì không.
chọc

@poke Khi tôi nói thêm trong câu trả lời, tôi đang sử dụng "mã hóa" ở đây để thể hiện cả "ánh xạ các ký tự đến các vị trí trên biểu đồ" và "biểu diễn các vị trí đó dưới dạng một chuỗi bit". Có thể có một thuật ngữ tốt hơn để sử dụng, nhưng tôi không chắc nó sẽ là gì.
IMSoP

3
Chà, bạn không thể chỉ sử dụng mã hóa mã hóa Viking với định nghĩa của riêng bạn. Xin lỗi vì đã gây chú ý ở đây, nhưng bạn không thể làm điều đó trong một câu trả lời mở ra với câu hỏi có chứa một quan niệm sai lầm trung tâm về Unicode là gì .
chọc

2
IMSoP (và @poke): Tôi hoàn toàn đồng ý với việc chọc vào việc sử dụng "mã hóa" có nghĩa là một cái gì đó khác với mã hóa, mặc dù tôi cũng đồng cảm với tình huống khó xử của IMSoP. Sở thích của tôi là đề cập đến Unicode như một bộ ký tự có nhiều mã hóa, trong khi thông thường, bộ ký tự và mã hóa được sử dụng thay thế cho nhau do hầu hết là mối quan hệ 1-1 (hoặc có thể là tất cả?).
Solomon Rutzky

2
Câu trả lời tốt. Tôi đặc biệt khuyên bạn nên thêm một liên kết đến Tối thiểu tuyệt đối Mỗi nhà phát triển phần mềm Tuyệt đối, Tích cực phải biết về Unicode và các bộ ký tự trong đó.
jpmc26
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.