ทำไมประเภทข้อมูล varchar อนุญาตให้ใช้ค่า unicode


17

ฉันมีตารางที่มีคอลัมน์ varchar มันช่วยให้ Trademark (ลิขสิทธิ์), ลิขสิทธิ์ (©) และตัวอักษร 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ตัวอักษร คำจำกัดความขัดแย้งกับคุณสมบัติของประเภทข้อมูล varchar หรือไม่ ผมอ่านคู่ของการเชื่อมโยงเช่นคนแรกและคนที่สอง แต่ฉันก็ยังไม่เข้าใจว่าทำไมจึงอนุญาตให้ใช้สตริง Unicode เมื่อคำจำกัดความระบุว่าอนุญาตเฉพาะค่าสตริงที่ไม่ใช่ Unicode เท่านั้น


12
อักขระทั้งหมดเป็นอักขระ Unicode
Martin Smith

Microsoft มักใช้ UNICODE เมื่อพวกเขาหมายถึง UTF-16 / UCS-2 ดังนั้นพวกเขาอาจไม่นับ UTF-8 เนื่องจาก UNICODE เป็นบริบท
CodesInChaos

1
@CodesInChaos: ฉันพยายามแยกความคิดเห็นของคุณ แต่ฉันกังวลว่าคุณสับสน Unicode กับการเข้ารหัส UTF-n ต่างๆ
การแข่งขัน Lightness กับโมนิก้า

1
@Martin Smith: หากอักขระทั้งหมดเป็นอักขระ Unicode แล้วทำไมนิยาม varchar ของ Microsoft บอกว่าอนุญาตให้ใช้ข้อมูลสตริงที่ไม่ใช่ Unicode ได้
พระอิศวร

2
การเข้ารหัสสำหรับอักขระใน varchar ไม่ใช่ Unicode แต่อักขระทั้งหมดมีอยู่ใน Unicode
Martin Smith

คำตอบ:


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 และ non-unicode เท่านั้นและ Trademark (™) ที่คุณใช้ไม่ใช่ unicode
sepupic

16
"Extended ASCII" เป็นคำที่ไม่ชัดเจนอย่างน่ากลัว มันจะมีประโยชน์มากกว่าถ้าคุณดูว่าการเข้ารหัสแบบ 8 บิตแบบใดที่ใช้งานจริง (ขึ้นอยู่กับการตั้งค่าภาษา / การเปรียบเทียบ) ฉันคาดเดารหัสของ Windows หน้า 1252ซึ่งไม่แน่นอนเข้ารหัส™เป็นตัวละคร 153
IMSoP

2
@sepupic ฉันคิดว่าคุณต้องอ่านเพิ่มเติมเกี่ยวกับความแตกต่างระหว่าง codepoints และ encodings วิกิพีเดียอาจช่วยได้ "การเข้ารหัสแผนที่ (อาจจะเป็นส่วนหนึ่งของ) ช่วงของยูนิโค้ดจุดรหัสลำดับของค่าในช่วงขนาดคงที่บางที่เรียกว่าค่ารหัส ." 8482 เป็น codepoint สำหรับ™ซึ่งสามารถเข้ารหัสเป็น \ x99 (153) ใน Windows-1252, เป็น \ xAA ใน MacRoman, เป็น \ xE2 \ x84 \ xA2 ใน UTF-8, ฯลฯ
อยากรู้อยากเห็น

7
ควรใช้ความระมัดระวังด้วยอักขระ 8 บิตที่สูงกว่า 127: รหัสใดที่สูงกว่า 127 สามารถแทนได้และขึ้นอยู่กับการเข้ารหัสที่ใช้ซึ่งจะแตกต่างกันไปขึ้นอยู่กับการเปรียบเทียบที่ใช้อยู่ ใน codepage 1252 unicode 8482 ถูกแมปกับ 153 ใน codepage 850 นั้นเป็นจุดที่ 214 ( Ö) และใน ISO-8859-1 (บางครั้งเรียกว่า Latin1) มันเป็นรหัสควบคุมที่ไม่มีการพิมพ์แทน หากคุณไม่ทราบว่าคุณจะใช้เพจรหัสเดียวกันเสมอจะปลอดภัยกว่าที่จะใช้อักขระ ANSI (127 หรือน้อยกว่า) หรือใช้ประเภท Unicode Codepage 1252 พบมากที่สุดใน SQL Server แต่ห่างไกลจากที่แพร่หลาย
David Spillett

4
@Shiva ผู้พัฒนาแน่นอนทุกขั้นต่ำของซอฟท์แวแน่นอนบวกต้องทราบเกี่ยวกับ Unicode และชุดตัวอักษร ASCII เป็นชุดย่อยของการเข้ารหัสจำนวนมากและเกือบทั้งหมดของการเข้ารหัสเหล่านั้นมีสัญลักษณ์ที่ไม่ใช่ ASCII และไม่ได้เป็น Unicode พร้อมกัน และ Unicode ก็มีการเข้ารหัสที่แตกต่างกันเช่นกัน (เช่น UTF-8, UTF-32 เป็นต้น)
jpmc26

7

จากความคิดเห็นฉันยอมรับ "Extended 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 ข้อมูล แต่เครื่องหมายการค้า (™) และลงทะเบียน (®) สัญลักษณ์ Unicode ตัวอักษร คำจำกัดความขัดแย้งกับคุณสมบัติของประเภทข้อมูล varchar หรือไม่

ในขณะที่คำตอบอื่น ๆ ไม่ถูกต้องฉันคิดว่ามันจะช่วยชี้ให้เห็นความสับสนในคำศัพท์พื้นฐาน ฉันได้เน้นสองคำในการอ้างอิงข้างต้นจากคำถามเป็นตัวอย่างของความสับสนนี้ เมื่อเอกสาร SQL Server พูดถึง Unicode และไม่ใช่ Unicode ข้อมูลที่พวกเขาจะไม่ได้พูดคุยเกี่ยวกับตัวละคร พวกเขากำลังพูดถึงลำดับของไบต์ที่เป็นตัวแทนของตัวละครบางตัว ความแตกต่างหลักระหว่างประเภท Unicode ( NCHAR, NVARCHAR, XMLและเลิก / ชั่วNTEXT) และประเภทที่ไม่ใช่ Unicode ( CHAR, VARCHARและเลิก / ชั่วTEXT) คือสิ่งที่ประเภทของลำดับไบต์ที่พวกเขาสามารถเก็บ

ประเภทที่ไม่ใช่ Unicode จัดเก็บหนึ่งในการเข้ารหัส 8 บิตหนึ่งในขณะที่ประเภท Unicode จะจัดเก็บการเข้ารหัส Unicode 16 บิตเดียว: UTF-16 Little Endian ดังที่ได้กล่าวไปแล้วคำตอบอื่น ๆ อักขระที่สามารถเก็บไว้ในการเข้ารหัส 8 บิต / ไม่ใช่ Unicode ขึ้นอยู่กับหน้ารหัสซึ่งกำหนดโดย Collation ในขณะที่คนอื่น ๆ ตั้งข้อสังเกตว่าค่า byte ของ "ตัวอักษร" สามารถแตกต่างกันไปตามหน้ารหัสที่พบในนั้นค่า byte สามารถแตกต่างกันไปในหน้ารหัสเดียวกันเมื่อจัดการกับหนึ่งในหลายหน้ารหัส EBCDIC (รูปแบบของ Windows- 1252) ซึ่งพบได้ใน SQL Server ที่เก่ากว่าเท่านั้นไม่ควรใช้จริง ๆ (เช่นชื่อที่ขึ้นต้นด้วยSQL_)

ดังนั้นคำจำกัดความที่ถูกต้อง: อักขระใด ๆ ที่คุณสามารถจัดการเพื่อจัดเก็บในรูปแบบที่ไม่ใช่ Unicode จะเป็น 8 บิตเสมอ (แม้ว่าพวกเขาจะใช้ค่า 8 บิตสองชุดในการรวมกันเป็น "ตัวละคร" เดียวซึ่งเป็นสิ่งที่ Double- อนุญาตให้ใช้ชุดอักขระไบต์ / โค้ดเพจ DBCS ได้) และ Unicode datatypes นั้นอยู่ที่ 16 บิตเสมอแม้ว่าบางครั้งพวกเขาก็ใช้ค่า 16- บิตสองค่าในการรวมกันเป็น "อักขระ" เดียว (เช่นคู่ตัวแทนแทนซึ่งจะแสดงถึงอักขระเสริม)

และเนื่องจาก SQL Server สนับสนุนการเข้ารหัส UTF-8 VARCHARและCHARประเภทข้อมูลตั้งแต่ SQL Server 2019

VARCHARไม่สามารถเรียกได้ว่า "non-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 ดังนั้นอย่าใช้) คือ "non-Unicode" แต่นั่นเป็นเพียงด้านเทคนิคและอ้างถึงว่าเป็น "ประเภทข้อมูล 8 บิต" นั้นถูกต้อง

NVARCHAR, NCHARและNTEXTสามารถอ้างถึงเป็น "UTF-16" หรือ "ประเภทข้อมูล 16 บิต" ฉันเชื่อว่า Oracle ใช้คำศัพท์เฉพาะ "Unicode-only" สำหรับNVARCHARแต่ก็ไม่ได้ตัดทอนความเป็นไปได้ของการใช้ UTF-8 (เช่นการเข้ารหัส Unicode) ซึ่งไม่สามารถใช้งานได้ สองตัวเลือกแรก

สำหรับรายละเอียดเกี่ยวกับการเข้ารหัส UTF-8 ใหม่โปรดดูโพสต์ของฉัน:

สนับสนุน UTF-8 ดั้งเดิมใน SQL Server 2019: Savior หรือ False Prophet?

ป.ล. ฉันกำลังทำงานอย่างช้า ๆ ในการปรับปรุงเอกสาร SQL Server เพื่อให้สอดคล้องกับการเปลี่ยนแปลงเหล่านี้

PPS Microsoft ได้อัปเดตบางหน้าด้วยข้อมูล UTF-8 แล้วรวมถึงเอกสารchar และ varchar ที่อ้างอิงในคำถาม ไม่มีวลี "ที่ไม่ใช่ Unicode" อีกต่อไป แต่นั่นเป็นเพียง FYI; มันไม่เปลี่ยนคำถามเนื่องจากนี่เป็นเรื่องเกี่ยวกับการเข้ารหัสที่ไม่ใช่ Unicode ที่มีอักขระที่คิดว่าผิดพลาดว่าเป็น Unicode เท่านั้น


3

คำถามมีความเข้าใจผิดที่สำคัญเกี่ยวกับ Unicode คืออะไร ชุดอักขระ Unicode พร้อมการเข้ารหัสเช่น UTF-8 และ UTF-16 เป็นหนึ่งในหลาย ๆ วิธีในการแสดงข้อความในคอมพิวเตอร์และเป็นหนึ่งในเป้าหมายที่จะแทนที่ชุดอักขระและการเข้ารหัสอื่น ๆ ทั้งหมด หาก "ข้อมูลที่ไม่ใช่ Unicode" หมายถึง "ตัวอักษรที่ไม่ได้อยู่ใน Unicode" ดังนั้นข้อความที่ฉันใช้ในคำตอบนี้ไม่สามารถเก็บไว้ในประเภทนั้นได้เพราะตัวอักษรทั้งหมดของตัวอักษรละตินและเครื่องหมายวรรคตอนทั่วไปที่ใช้ในภาษาอังกฤษทุกวัน รวมอยู่ใน Unicode

การแทนข้อความสามารถพิจารณาได้อย่างกว้าง ๆ ในสองส่วน: ชุดอักขระที่แมปอักขระที่แตกต่างกัน (ตัวอักษรตัวเลขสัญลักษณ์ ฯลฯ ) กับตัวเลขในแผนภูมิอ้างอิง และการเข้ารหัสแสดงตัวเลขเหล่านั้นเป็นรูปแบบของบิต (บนดิสก์ผ่านการเชื่อมต่อเครือข่าย ฯลฯ ) ที่นี่เราส่วนใหญ่เกี่ยวข้องกับส่วนแรก: ตัวละครที่จะถูกระบุไว้ในชาร์ตสำหรับชุดตัวละครเฉพาะ

เนื่องจาก Unicode มีจุดมุ่งหมายที่จะมีตัวเลข (ซึ่งเรียกว่า "code points") สำหรับตัวละครทุกตัวในโลกการอ้างอิงเช่น Wikipedia มักจะอ้างถึงตำแหน่ง Unicode ของตัวละครเป็นข้อมูลอ้างอิงมาตรฐาน อย่างไรก็ตามนั่นไม่ได้หมายความว่าชุดอักขระอื่นไม่มีการแมปสำหรับอักขระเดียวกันนั้น

หนึ่งในชุดอักขระที่เก่าแก่ที่สุดและง่ายที่สุด (และการเข้ารหัส) ที่ยังคงใช้งานอยู่คือ ASCII ซึ่งมีการแมปสำหรับอักขระที่แตกต่างกัน 128 ตัว (0 ถึง 127) เนื่องจากมันใช้ 7 บิตในการเข้ารหัสอักขระแต่ละตัว เนื่องจากสิ่งนี้ไม่รวมอักขระเน้นเสียงและสัญลักษณ์ทั่วไปการเข้ารหัสในภายหลังจึงใช้ 8 บิตและแมปอักขระ 128 ตัวแรกเหมือนกันโดยเพิ่มชุดอักขระตามตำแหน่งที่กรอก 128 ถึง 255 ซึ่งโดดเด่นในหมู่เหล่านี้คือISO 8859-1และISO 8859- มาตรฐาน 15และไมโครซอฟท์เฉพาะของ Windows หน้ารหัส 1252

ดังนั้นเพื่อที่จะกลับไปยัง MS SQL Server มาเป็น "สายอักขระ Unicode" ในขณะที่เก็บไว้ในnchar, nvarcharหรือntextคอลัมน์สามารถเป็นตัวแทนของทุกตัวละครแมปในชุดอักขระ Unicode เพราะมันใช้ Unicode เข้ารหัสในการจัดเก็บข้อมูล A "ไม่ใช่ Unicode สตริง" ในขณะที่เก็บไว้ในchar, varcharหรือtextคอลัมน์สามารถเป็นตัวแทนเพียงตัวละครแมปในบางเข้ารหัสอื่นทุกสิ่งที่คุณสามารถเก็บไว้ในคอลัมน์ที่ไม่ใช่ Unicode ยังสามารถเก็บไว้ในคอลัมน์ Unicode ได้ แต่ไม่สามารถสลับกันได้

ที่จะรู้ว่าตัวอักษรที่คุณสามารถจัดเก็บคุณจะต้องรู้ว่า "การเปรียบเทียบ" ในการใช้งานซึ่งสั่งการสิ่งที่ไมโครซอฟท์หมายถึงว่าเป็น "หน้ารหัส" ในขณะที่อธิบายในหน้าเอกสารอ้างอิงนี้ไมโครซอฟท์ เป็นไปได้ว่าในกรณีของคุณที่คุณกำลังใช้รหัสทั่วไปมากหน้า 1252 ซึ่งฉันได้กล่าวถึงก่อนหน้านี้

อักขระที่คุณกล่าวถึงมีอยู่ในทั้ง Unicode และ Code Page 1252:

  • เครื่องหมายการค้า (™) ปรากฏใน Unicode ที่ตำแหน่ง 8482 และใน CP1252 ที่ตำแหน่ง 153
  • ลงทะเบียน (®) ตามที่ปรากฏขึ้นทั้งใน Unicode และ CP1252 ที่ตำแหน่ง 174

3
“ Unicode เป็นหนึ่งในหลาย ๆ วิธีในการเข้ารหัสข้อความเพื่อใช้ในคอมพิวเตอร์” - ไม่ถูกต้อง Unicode เป็นเพียงชุดของตัวละครและสัญลักษณ์ที่ตัวละครแต่ละตัวมีจุดรหัสเฉพาะของตัวเองซึ่งเป็นเพียงตัวเลข จากนั้นงานของการเข้ารหัสคือการจับคู่รหัสเหล่านั้นกับลำดับไบต์ UTF-8 และ UTF-16 กำลังเข้ารหัส Unicode ไม่ใช่
สะกิด

@poke เมื่อฉันพูดต่อไปในคำตอบฉันใช้ "การเข้ารหัส" ที่นี่เพื่อแสดงทั้ง "การทำแผนที่ของตัวละครไปยังตำแหน่งบนแผนภูมิ" และ "การเป็นตัวแทนของตำแหน่งเหล่านั้นเป็นลำดับของบิต" อาจมีคำที่ดีกว่าที่จะใช้ แต่ฉันไม่แน่ใจว่ามันจะเป็นอย่างไร
IMSoP

3
คุณไม่สามารถใช้“ การเข้ารหัส” กับคำจำกัดความของคุณเองได้ ขออภัยที่จะ nitpicking ที่นี่ แต่คุณไม่สามารถทำเช่นนั้นในคำตอบที่เปิดกับ“คำถามมีความเข้าใจผิดเกี่ยวกับสิ่งที่กลาง Unicode คือ”
กระตุ้น

2
IMSoP (และ @poke): ฉันเห็นด้วยกับการกระตุ้นเกี่ยวกับการใช้ "การเข้ารหัส" เพื่อหมายถึงสิ่งอื่นนอกเหนือจากการเข้ารหัสแม้ว่าฉันจะเห็นด้วยกับภาวะที่กลืนไม่เข้าคายไม่ออกของ IMSoP การตั้งค่าของฉันคือการอ้างถึง Unicode เป็นชุดอักขระที่มีการเข้ารหัสหลายตัวในขณะที่โดยทั่วไปจะใช้ชุดอักขระและการเข้ารหัสแทนกันเนื่องจากมีความสัมพันธ์แบบ 1 ต่อ 1 มากที่สุด (หรืออาจทั้งหมด)
โซโลมอน Rutzky

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.