¿Por qué el tipo de datos varchar permite valores unicode?

17

Tengo una mesa con una columna varchar. Está permitiendo Trademark (™), copyright (©) y otros caracteres Unicode como se muestra a continuación.

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

Pero la definición de varchar dice que permite datos de cadena no unicode. Pero los símbolos Trademark (™) y Registered (®) son caracteres Unicode . ¿La definición contradice la propiedad del tipo de datos varchar? Leí un par de enlaces como el primero y el segundo . Pero aún así no podía entender por qué permite la cadena unicode cuando la definición dice que solo permite valores de cadena no unicode.

Shiva
fuente
12
Todos los personajes son caracteres Unicode.
Martin Smith
Microsoft a menudo usa UNICODE cuando se refieren a UTF-16 / UCS-2. Por lo tanto, es posible que ni siquiera cuenten con UTF-8, ya que UNICODE es un contexto.
CodesInChaos
1
@CodesInChaos: Luché por analizar tu comentario, pero me preocupa que estés confundiendo a Unicode con las diversas codificaciones UTF-n.
Lightness compite con Monica el
1
@ Martin Smith: Si todos los caracteres son caracteres Unicode, ¿por qué la definición de Microsoft varchar dice que permite datos de cadena no Unicode?
Shiva
2
la codificación de los caracteres en varchar no es unicode pero todos los caracteres existen en unicode
Martin Smith

Respuestas:

15

Pero los símbolos Trademark (™) y Registered (®) son caracteres Unicode.

Estás equivocado aquí. Sus cadenas contienen solo asciicaracteres.

Aquí hay una prueba simple que muestra que tus personajes son todos ascii (+ algunos extended asciicon códigos ascii entre 128 y 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;

Aquí puede ver claramente que todos sus personajes están codificados con 1 byte:

ingrese la descripción de la imagen aquí

Sí, no son caracteres ascii puros, sino ASCII extendido .

Aquí te muestro el verdadero carácter unicode Trademark(™)y su código y representación 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;

ingrese la descripción de la imagen aquí

Finalmente, puede ver que el Trademark(™)carácter unicode tiene código 8482 y no 153:

select nchar(8482), nchar(153)
sepupic
fuente
1
Pero no hay una palabra "ASCII" en el artículo que mencionó, solo se refieren a caracteres unicode y no unicode, y Trademark (™) que utilizó no era unicode.
Sepupic
16
"ASCII extendido" es un término terriblemente ambiguo. Sería más útil observar qué codificación de 8 bits se usa realmente (¿se basa en la configuración regional / de clasificación?). Supongo que la página de códigos de Windows 1252 , que de hecho codifica ™ como carácter 153.
IMSoP
2
@sepupic Creo que necesita leer más sobre la diferencia entre los puntos de código y las codificaciones. Wikipedia puede ayudar. "Una codificación asigna (posiblemente un subconjunto de) el rango de puntos de código Unicode a secuencias de valores en algún rango de tamaño fijo, denominados valores de código ". 8482 es el punto de código para ™, que puede ser codificada como \ x99 (153) en Windows-1252, como \ Xaa en MacRoman, como \ XE2 \ x84 \ xa2 en UTF-8, etc.
curiousdannii
77
Se debe tener cuidado con los caracteres de 8 bits por encima de 127: lo que representa cada código por encima de 127 puede cambiar y cambiará según la codificación en uso, que variará según la colación que se use. En la página de códigos 1252, el Unicode 8482 se asigna a 153. En la página de códigos 850, 214 ( Ö) toma ese lugar y en ISO-8859-1 (a veces llamado Latin1) es un código de control sin representación imprimible. A menos que sepa que siempre usará la misma página de códigos, es más seguro adherirse a los caracteres ANSI (127 o menos) o usar tipos Unicode. La página de códigos 1252 es más común en SQL Server pero está lejos de ser omnipresente.
David Spillett
44
@Shiva El mínimo absoluto que todo desarrollador de software debe saber de manera absoluta y positiva sobre los conjuntos de caracteres y Unicode . ASCII es un subconjunto de muchas codificaciones, y casi todas esas codificaciones contienen símbolos que no son ASCII y simultáneamente no son Unicode. Y Unicode también tiene muchas codificaciones diferentes (como UTF-8, UTF-32, etc.).
jpmc26
7

De los comentarios, estoy de acuerdo que "ASCII extendido" es un término realmente malo que en realidad significa una página de códigos que asigna caracteres / puntos de código en el rango de 128-255, más allá del rango de puntos de código estándar de 0-127 definido por ASCII.

SQL Server admite muchas páginas de códigos a través de intercalaciones. Los caracteres no ASCII se pueden almacenar en varchar siempre que la clasificación subyacente admita el carácter.

El carácter '™' se puede almacenar en columnas varchar / char cuando la página de códigos de ordenación de SQL Server es 1250 o superior. La consulta a continuación enumerará estos:

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

Pero solo un subconjunto de estos también admite el carácter '©', por lo que la clasificación de columnas deberá ser una de las siguientes para admitir ambos:

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;
Dan Guzman
fuente
4

Pero la definición de varchar dice que permite datos de cadena no unicode . Pero los símbolos Trademark (™) y Registered (®) son caracteres Unicode . ¿La definición contradice la propiedad del tipo de datos varchar?

Si bien las otras respuestas no son incorrectas, creo que ayudaría señalar una confusión en la terminología básica. He enfatizado dos palabras en la cita anterior de la pregunta como un ejemplo de esta confusión. Cuando la documentación de SQL Server habla de no Unicode Unicode y datos , que están no hablan de los personajes . Están hablando de las secuencias de bytes que representan ciertos caracteres. La principal diferencia entre los tipos Unicode ( NCHAR, NVARCHAR, XML, y el desuso / malos NTEXT) y los tipos no Unicode ( CHAR, VARCHARy el desuso / mal TEXT) es lo que los tipos de secuencias de bytes que pueden almacenar.

Los tipos no Unicode almacenan una de varias codificaciones de 8 bits, mientras que los tipos Unicode almacenan una sola codificación Unicode de 16 bits: UTF-16 Little Endian. Como las otras respuestas han mencionado, qué caracteres se pueden almacenar en una codificación de 8 bits / no Unicode depende de la página de códigos, que está determinada por la Clasificación. Mientras que otros han notado que el valor de byte de un "carácter" puede variar entre las páginas de códigos en las que se encuentra, el valor de byte incluso puede variar dentro de la misma página de códigos cuando se trata de una de las varias páginas de códigos EBCDIC (variaciones de Windows- 1252), que solo se encuentran en las clasificaciones de SQL Server más antiguas, no deberían usarse realmente (es decir, aquellas que tienen nombres que comienzan con SQL_).

Por lo tanto, la definición es precisa: los caracteres que puede almacenar en un tipo que no sea Unicode son siempre de 8 bits (incluso si usan dos valores de 8 bits en combinación como un solo "carácter", que es lo que el Double- El conjunto de caracteres de bytes / las páginas de códigos DBCS lo permiten). Y los tipos de datos Unicode son siempre de 16 bits, incluso si a veces usan dos valores de 16 bits en combinación como un solo "carácter" (es decir, un par sustituto que a su vez representa un carácter suplementario).

Y, debido a que SQL Server admite de forma nativa la codificación UTF-8 VARCHARy los CHARtipos de datos a partir de SQL Server 2019,

VARCHARya no puede denominarse "no Unicode". Entonces, comenzando con la primera versión beta pública de SQL Server 2019 en septiembre de 2018, deberíamos referirnos VARCHARcomo un "tipo de datos de 8 bits", incluso cuando se habla en términos de versiones anteriores a SQL Server 2019. Esta terminología es válida para los 4 tipos de codificaciones que se pueden usar con VARCHAR:

  1. ASCII extendido
  2. Conjuntos de caracteres de doble byte (DBCS)
  3. EBCDIC
  4. UTF-8 (Unicode)

Solo el TEXTtipo de datos (en desuso a partir de SQL Server 2005, así que no lo use) es "no Unicode", pero eso es solo un tecnicismo, y referirse a él como un "tipo de datos de 8 bits" es exacto.

NVARCHAR, NCHARy NTEXTpuede denominarse "UTF-16" o "tipo de datos de 16 bits". Oracle, creo, usa la terminología de "Unicode-only" para NVARCHAR, pero eso no descarta claramente la posibilidad de usar UTF-8 (también una codificación Unicode), que no funcionará, así que probablemente sea mejor quedarse con Las dos primeras opciones.

Para obtener detalles sobre las nuevas codificaciones UTF-8, consulte mi publicación:

Soporte nativo de UTF-8 en SQL Server 2019: ¿Salvador o falso profeta?

PD: Estoy trabajando lentamente para actualizar la documentación de SQL Server para reflejar estos cambios.

PPS Microsoft ya ha actualizado algunas páginas con información de UTF-8, incluida la documentación de char y varchar a la que se hace referencia en la pregunta. Ya no contiene la frase "no Unicode". Pero eso es solo un FYI; no cambia la pregunta, ya que se trata de codificaciones no Unicode que contienen caracteres que se pensaban erróneamente que eran solo Unicode.

Solomon Rutzky
fuente
3

La pregunta contiene un error central sobre lo que es Unicode. El conjunto de caracteres Unicode, junto con sus codificaciones como UTF-8 y UTF-16, es una de las muchas formas de representar texto en una computadora, y una cuyo objetivo es reemplazar todos los demás conjuntos de caracteres y codificaciones. Si "datos que no son Unicode" significan "caracteres no presentes en Unicode", entonces ninguno de los textos que he usado en esta respuesta podría almacenarse en ese tipo, porque todas las letras del alfabeto latino y la puntuación común utilizada en inglés cotidiano son incluido en Unicode.

Las representaciones de texto se pueden considerar ampliamente en dos partes: un conjunto de caracteres que asigna los diferentes caracteres (letras, dígitos, símbolos, etc.) a números en un gráfico de referencia; y una codificación que representa esos números como patrones de bits (en el disco, a través de una conexión de red, etc.). Aquí nos ocupamos principalmente de la primera parte: qué caracteres se enumeran en los gráficos para un conjunto de caracteres en particular.

Dado que Unicode tiene como objetivo tener números (que llama "puntos de código") para cada personaje en el mundo, las referencias como Wikipedia a menudo se referirán a la posición Unicode de un personaje como información estándar de referencia. Sin embargo, eso no significa que otros conjuntos de caracteres no tengan una asignación para ese mismo carácter.

Uno de los conjuntos de caracteres (y codificaciones) más antiguos y más simples que todavía se usan es ASCII, que tiene asignaciones para 128 caracteres diferentes (0 a 127), ya que utiliza 7 bits para codificar cada carácter. Dado que esto excluye muchos caracteres acentuados y símbolos comunes, las codificaciones posteriores usan 8 bits y mapean los mismos primeros 128 caracteres, agregando al conjunto de caracteres al llenar las posiciones 128 a 255. Entre estos destacan los estándares ISO 8859-1 e ISO 8859- 15 , y la página de códigos de Windows específica de Microsoft 1252 .

Por lo tanto, para volver a MS SQL Server: una "cadena Unicode", como se almacena en una nchar, nvarcharo ntextcolumna, puede representar a todos los caracteres asignados en el conjunto de caracteres Unicode, ya que utiliza una codificación Unicode para almacenar los datos. Una "cadena no Unicode", como se almacena en una char, varcharo textcolumna, puede representar sólo los caracteres mapeadas en alguna otra codificación . Cualquier cosa que pueda almacenar en una columna que no sea Unicode también se puede almacenar en una columna Unicode, pero no al revés.

Para saber exactamente qué caracteres puede almacenar, debe conocer la "clasificación" en uso, que dicta lo que Microsoft denomina "página de códigos", como se explica en esta página de referencia de Microsoft . En su caso, es probable que esté utilizando el Código de página 1252, que mencioné anteriormente.

Los caracteres que mencionó existen tanto en Unicode como en la página de códigos 1252:

  • Trademark (™) aparece en Unicode en la posición 8482 y en CP1252 en la posición 153
  • Registrado (®), como sucede, aparece tanto en Unicode como en CP1252 en la posición 174
IMSoP
fuente
3
"Unicode es una de las muchas formas de codificar texto para usar en una computadora" - Eso no es correcto. Unicode es solo una colección de caracteres y símbolos, donde cada personaje tiene su propio punto de código único , que es solo un número. El trabajo de una codificación es hacer coincidir esos puntos de código con una secuencia de bytes. UTF-8 y UTF-16 son codificaciones, Unicode no lo es.
meter
@poke A medida que avanzo en la respuesta, estoy usando "codificación" aquí para representar tanto el "mapeo de caracteres a posiciones en un gráfico" como las "representaciones de esas posiciones como una secuencia de bits". Quizás haya un mejor término para usar, pero no estoy seguro de cuál sería.
IMSoP
3
Bueno, no puedes simplemente usar "codificación" con tu propia definición. Perdón por estar aquí, pero no puedes hacerlo en una respuesta que se abre con "la pregunta contiene un error central sobre lo que es Unicode" .
meter
2
IMSoP (y @poke): estoy completamente de acuerdo con poke con respecto a la extralimitación en el uso de "codificación" para significar algo más que codificación, aunque también simpatizo con el dilema de IMSoP. Mi preferencia es referirme a Unicode como un conjunto de caracteres que tiene múltiples codificaciones, mientras que típicamente el conjunto de caracteres y la codificación se usan indistintamente debido a que es una relación de 1 a 1 la mayoría (¿o tal vez todo?) Del tiempo.
Solomon Rutzky