Perché il tipo di dati varchar consente valori unicode?


17

Ho una tabella con una colonna varchar. Consentono marchi (™), copyright (©) e altri caratteri Unicode come mostrato di seguito.

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

Ma la definizione di varchar dice che consente dati di stringa non unicode. Ma i simboli Marchio (™) e Registrato (®) sono caratteri Unicode . La definizione contraddice la proprietà del tipo di dati varchar? Ho letto un paio di link come il primo e il secondo . Ma ancora non riuscivo a capire perché consente la stringa unicode quando la definizione dice che consente solo valori di stringa non unicode.


12
Tutti i personaggi sono caratteri Unicode.
Martin Smith,

Microsoft utilizza spesso UNICODE quando intendono UTF-16 / UCS-2. Quindi potrebbero anche non contare UTF-8 poiché UNICODE è un contesto.
Codici A Caos

1
@CodesInChaos: ho faticato ad analizzare il tuo commento, ma temo che tu confonda Unicode con le varie codifiche UTF-n.
Lightness Races con Monica

1
@Martin Smith: se tutti i caratteri sono caratteri Unicode, allora perché la definizione di Microsoft Varchar afferma che consente dati stringa non Unicode?
Shiva,

2
la codifica per i personaggi in varchar non è unicode ma tutti i personaggi esistono in unicode
Martin Smith

Risposte:


15

Ma i simboli Marchio (™) e Registrato (®) sono caratteri Unicode.

Ti sbagli qui. Le tue stringhe contengono solo asciicaratteri.

Ecco un semplice test che ti mostra che i tuoi personaggi sono tutti ascii (+ alcuni extended asciicon codici ascii tra 128 e 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;

Qui puoi vedere chiaramente che tutti i tuoi personaggi sono codificati a 1 byte:

inserisci qui la descrizione dell'immagine

Sì, non sono caratteri ASCII puri ma sono ASCII estesi .

Qui ti mostro il vero carattere Unicode Trademark(™)e il suo codice e la sua rappresentazione binaria:

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;

inserisci qui la descrizione dell'immagine

Infine, puoi vedere che il Trademark(™)carattere unicode ha il codice 8482 e non 153:

select nchar(8482), nchar(153)

1
Ma non c'è alcuna parola "ASCII" nell'articolo che hai citato, parlano solo di caratteri unicode e non unicode e il marchio di fabbrica (™) che hai usato non era unicode.
sepupic

16
"Extended ASCII" è un termine orribilmente ambiguo. Sarebbe più utile esaminare quale codifica a 8 bit viene effettivamente utilizzata (si basa sulle impostazioni locali / di confronto?). Sto indovinando la pagina di codice 1252 di Windows , che in effetti codifica ™ come carattere 153.
IMSoP

2
@sepupic Penso che devi leggere di più sulla differenza tra punti di codifica e codifiche. Wikipedia può aiutare. "Una codifica mappa (possibilmente un sottoinsieme di) l'intervallo del codice Unicode punta a sequenze di valori in un intervallo di dimensioni fisse, denominato valori di codice ." 8482 è il punto di codice per ™, che può essere codificato come \ x99 (153) in Windows-1252, come \ xAA in MacRoman, come \ xE2 \ x84 \ xA2 in UTF-8, ecc.
curiousdannii

7
Bisogna fare attenzione con i caratteri a 8 bit sopra 127: ciò che ogni codice sopra 127 rappresenta e può cambiare a seconda della codifica in uso che varierà a seconda delle regole di confronto in uso. Nella codepage 1252 unicode 8482 è mappato su 153. Nella codepage 850 quel punto è preso da 214 ( Ö) e in ISO-8859-1 (a volte chiamato Latin1) è un codice di controllo senza rappresentazione stampabile. A meno che tu non sappia che utilizzerai sempre la stessa tabella codici, è più sicuro attenersi ai caratteri ANSI (127 o meno) o utilizzare i tipi Unicode. La tabella codici 1252 è più comune in SQL Server ma tutt'altro che onnipresente.
David Spillett,

4
@Shiva Il minimo assoluto Ogni sviluppatore di software deve assolutamente conoscere positivamente Unicode e set di caratteri . ASCII è un sottoinsieme di molte codifiche e quasi tutte queste codifiche contengono simboli non ASCII e non sono contemporaneamente Unicode. E Unicode ha anche molte codifiche diverse (come UTF-8, UTF-32, ecc.).
jpmc26

7

Dai commenti, sono d'accordo che "ASCII esteso" è un termine davvero brutto che in realtà significa una tabella codici che mappa caratteri / punti di codice nell'intervallo 128-255, oltre l'intervallo di punti di codice standard 0-127 definito da ASCII.

SQL Server supporta molte pagine di codice tramite regole di confronto. I caratteri non ASCII possono essere memorizzati in varchar purché le regole di confronto sottostanti supportino il carattere.

Il carattere '™' può essere archiviato in colonne varchar / char quando la tabella codici di confronto SQL Server è 1250 o superiore. La seguente query elencherà questi:

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

Ma solo un sottoinsieme di questi supporta anche il carattere "©", quindi le regole di confronto delle colonne dovranno essere una delle seguenti per supportare entrambi:

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

Ma la definizione di varchar dice che consente dati di stringa non unicode . Ma i simboli Marchio (™) e Registrato (®) sono caratteri Unicode . La definizione contraddice la proprietà del tipo di dati varchar?

Mentre le altre risposte non sono errate, penso che sarebbe utile sottolineare una confusione nella terminologia di base. Ho sottolineato due parole nella citazione sopra dalla domanda come esempio di questa confusione. Quando la documentazione di SQL Server parla di non Unicode Unicode e dati , sono non parlando dei personaggi . Stanno parlando delle sequenze di byte che rappresentano determinati caratteri. La differenza principale tra i tipi Unicode ( NCHAR, NVARCHAR, XML, e la deprecata / cattivi NTEXT) ed i tipi non Unicode ( CHAR, VARCHARe la deprecata / maligno TEXT) è ciò tipi di sequenze di byte possono immagazzinare.

I tipi non Unicode memorizzano una delle numerose codifiche a 8 bit, mentre i tipi Unicode memorizzano una singola codifica Unicode a 16 bit: UTF-16 Little Endian. Come hanno già detto le altre risposte, quali caratteri possono essere memorizzati in una codifica a 8 bit / non Unicode dipende dalla tabella codici, che è determinata dal confronto. Mentre altri hanno notato che il valore in byte di un "carattere" può variare attraverso le code page su cui si trova, il valore in byte può persino variare all'interno della stessa code page quando si ha a che fare con una delle numerose code page EBCDIC (varianti di Windows- 1252), che si trovano solo nelle versioni precedenti, non dovrebbero essere realmente utilizzate le regole di confronto di SQL Server (ovvero quelle che hanno nomi che iniziano con SQL_).

Quindi, la definizione è accurata: qualunque carattere tu riesca a memorizzare in un tipo non Unicode è sempre a 8 bit (anche se usano due valori a 8 bit in combinazione come un singolo "carattere", che è quello che Double- Set di caratteri byte / Le tabelle codici DBCS lo consentono). E i tipi di dati Unicode sono sempre a 16 bit, anche se a volte usano due valori a 16 bit in combinazione come un singolo "carattere" (cioè una coppia surrogata che a sua volta rappresenta un carattere supplementare).

E, grazie al supporto nativo di SQL Server della codifica UTF-8 per VARCHARe CHARtipi di dati a partire da SQL Server 2019,

VARCHARnon può più essere definito "non Unicode". Quindi, a partire dalla prima beta pubblica di SQL Server 2019 a settembre 2018, dovremmo fare riferimento a VARCHARun "tipo di dati a 8 bit", anche quando si parla in termini di versioni precedenti a SQL Server 2019. Questa terminologia vale per tutti e 4 i tipi di codifiche che possono essere utilizzate con VARCHAR:

  1. ASCII esteso
  2. Set di caratteri a doppio byte (DBCS)
  3. EBCDIC
  4. UTF-8 (Unicode)

Solo il TEXTtipo di dati (obsoleto a partire da SQL Server 2005, quindi non utilizzarlo) è "non Unicode", ma è solo un tecnicismo e il riferimento ad esso come "tipo di dati a 8 bit" è accurato.

NVARCHAR, NCHARe NTEXTpuò essere indicato come "UTF-16" o "tipo di dati a 16 bit". Oracle, credo, utilizza la terminologia di "Solo Unicode" per NVARCHAR, ma ciò non esclude chiaramente la possibilità di utilizzare UTF-8 (anche una codifica Unicode), che non funzionerà, quindi probabilmente è meglio attenersi a le prime due opzioni.

Per i dettagli sulle nuove codifiche UTF-8, vedere il mio post:

Supporto nativo UTF-8 in SQL Server 2019: Salvatore o Falso profeta?

PS Sto lentamente lavorando sulla mia strada aggiornando la documentazione di SQL Server per riflettere queste modifiche.

PPS Microsoft ha già aggiornato alcune pagine con le informazioni UTF-8, inclusa la documentazione char e varchar a cui fa riferimento la domanda. Non contiene più la frase "non Unicode". Ma questo è solo un FYI; non cambia la domanda poiché si tratta di codifiche non Unicode contenenti caratteri che erroneamente si pensava fossero solo Unicode.


3

La domanda contiene un malinteso centrale su cosa sia Unicode. Il set di caratteri Unicode, insieme alle sue codifiche come UTF-8 e UTF-16, è uno dei molti modi di rappresentare il testo in un computer e uno il cui scopo è quello di sostituire tutti gli altri set di caratteri e codifiche. Se "dati non Unicode" significano "caratteri non presenti in Unicode", nessuno dei testi che ho usato in questa risposta potrebbe essere memorizzato in quel tipo, perché tutte le lettere dell'alfabeto latino e la punteggiatura comune usate nell'inglese quotidiano sono incluso in Unicode.

Le rappresentazioni di testo possono essere ampiamente pensate in due parti: un set di caratteri che associa i diversi caratteri (lettere, cifre, simboli, ecc.) Ai numeri su un grafico di riferimento; e una codifica che rappresenta quei numeri come schemi di bit (su disco, tramite una connessione di rete, ecc.). Qui ci occupiamo principalmente della prima parte: quali personaggi sono elencati nelle classifiche per un determinato set di caratteri.

Dato che Unicode mira ad avere numeri (che chiama "punti di codice") per ogni personaggio del mondo, riferimenti come Wikipedia spesso faranno riferimento alla posizione Unicode di un personaggio come un'informazione standard di riferimento. Tuttavia, ciò non significa che anche altri set di caratteri non abbiano una mappatura per quello stesso personaggio.

Uno dei set di caratteri (e codifiche) più antichi e semplici ancora in uso è ASCII, che ha mappature per 128 caratteri diversi (da 0 a 127), poiché utilizza 7 bit per codificare ciascun carattere. Poiché ciò esclude molti caratteri accentati e simboli comuni, le codifiche successive usano 8 bit e mappano gli stessi primi 128 caratteri, aggiungendo al set di caratteri riempiendo le posizioni da 128 a 255. Notevoli tra questi sono gli standard ISO 8859-1 e ISO 8859- 15 e la pagina di codice Windows specifica per Microsoft 1252 .

Quindi, per tornare a MS SQL Server: una "stringa Unicode", come memorizzata in una nchar, nvarcharo ntextcolonna, può rappresentare tutti i caratteri mappati nel set di caratteri Unicode, perché utilizza una codifica Unicode per memorizzare i dati. A "stringa non Unicode", memorizzate in una char, varcharo textcolonna, può rappresentare solo i caratteri mappati in altre codifiche . Tutto ciò che è possibile memorizzare in una colonna non Unicode può anche essere archiviato in una colonna Unicode, ma non viceversa.

Per sapere esattamente quali caratteri è possibile memorizzare, è necessario conoscere le "regole di confronto" in uso, che determinano ciò che Microsoft chiama "codepage", come spiegato in questa pagina di riferimento di Microsoft . Nel tuo caso è probabile che tu stia utilizzando il codice pagina molto comune, che ho citato in precedenza.

I caratteri che hai citato esistono sia in Unicode che in Code Page 1252:

  • Il marchio (™) appare in Unicode in posizione 8482 e in CP1252 in posizione 153
  • Registered (®), come accade, appare sia in Unicode che in CP1252 in posizione 174

3
"Unicode è uno dei molti modi per codificare il testo da utilizzare in un computer" - Non è corretto. Unicode è solo una raccolta di caratteri e simboli, in cui ogni personaggio ha il suo punto di codice unico che è solo un numero. Il compito di una codifica è quindi far corrispondere quei punti di codice a una sequenza di byte. UTF-8 e UTF-16 sono codifiche, Unicode no.
colpire

@poke Mentre proseguo nella risposta, sto usando "codifica" qui per rappresentare sia la "mappatura dei caratteri alle posizioni su un grafico" sia le "rappresentazioni di quelle posizioni come una sequenza di bit". Forse c'è un termine migliore da usare, ma non sono sicuro di come sarebbe.
IMSoP

3
Bene, non puoi semplicemente usare la "codifica" con la tua definizione. Mi dispiace essere pignolo qui, ma non puoi farlo in una risposta che si apre con "la domanda contiene un malinteso centrale su cosa sia Unicode" .
colpì il

2
IMSoP (e @poke): sono completamente d'accordo con il poke riguardo al superamento sull'uso della "codifica" per significare qualcosa di diverso dalla codifica, anche se sono anche solidale con il dilemma di IMSoP. La mia preferenza è di fare riferimento a Unicode come un set di caratteri che ha più codifiche, mentre in genere set di caratteri e codifica sono usati in modo intercambiabile a causa di una relazione 1 a 1 la maggior parte (o forse tutti?) Del tempo.
Solomon Rutzky,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.