@gbn ya explicó la razón básica y la solución, pero la razón específica del comportamiento que está viendo es esta:
- Está utilizando un
VARCHAR
literal (sin N
prefijo) en lugar de un NVARCHAR
literal (cadena con N
prefijo), por lo tanto, el carácter Unicode se convertirá en VARCHAR
.
VARCHAR
es una codificación de 8 bits que, en la mayoría de los casos, es un byte por carácter, pero también puede tener dos bytes por carácter. Por otro lado, NVARCHAR
es una codificación de 16 bits (UTF-16 Little Endian) que tiene dos bytes o cuatro bytes por carácter.
- Debido a la diferencia en el número de bytes disponibles para usar para asignar caracteres, las codificaciones de 8 bits son, por su propia naturaleza, mucho más limitadas en el número de caracteres que se pueden asignar.
VARCHAR
los datos son de hasta 256 caracteres para juegos de caracteres de un solo byte (la mayoría de ellos) y hasta 65.536 caracteres para juegos de caracteres de doble byte (solo algunos de estos). Por otro lado, los NVARCHAR
datos pueden mapear un poco más de 1.1 millones de caracteres Unicode (aunque actualmente menos de 250k mapeados).
- Debido a la cantidad limitada de asignaciones que se pueden hacer con 8 bits /
VARCHAR
datos, se agrupan diferentes agrupaciones de caracteres (en función del idioma / cultura) en varias "páginas de códigos" (es decir, conjuntos de caracteres)
- Cada clasificación especifica qué página de códigos, si hay alguna, usar para los
VARCHAR
datos ( NVARCHAR
son todos los caracteres)
- Al convertir un literal de cadena o variable de
NVARCHAR
(es decir, Unicode / UTF-16 / todos los caracteres) a VARCHAR
(conjunto de caracteres basado en la página de códigos que se especifica en la mayoría de las intercalaciones), se utiliza la intercalación predeterminada de la base de datos
- Si la página de códigos de la clasificación utilizada para la conversión no contiene el mismo carácter, pero contiene una asignación de "mejor ajuste", se utilizará la asignación de "mejor ajuste".
- Si la página de códigos de la clasificación utilizada para la conversión no contiene el mismo carácter o contiene una asignación de "mejor ajuste", entonces se usará el carácter de "reemplazo" predeterminado (más comúnmente
?
).
Por lo tanto, lo que está viendo es una NVARCHAR
de VARCHAR
conversión debido a que falta el N
prefijo en el literal de cadena. Y, la página de códigos de la clasificación predeterminada para la base de datos no contiene exactamente el mismo carácter, pero se encontró una asignación de "mejor ajuste", razón por la cual está obteniendo un en 2
lugar de un ?
.
Puede ver este efecto haciendo la siguiente prueba simple:
SELECT '₂', N'₂';
Devoluciones:
2 ₂
Para ser claros, SI la página de códigos de la clasificación predeterminada para la base de datos contenía exactamente el mismo carácter, entonces se habría traducido al mismo carácter en esa página de códigos. Y, luego, en su caso, dado que está almacenando en una NVARCHAR
columna, se habría traducido nuevamente, al carácter original de Unicode. El último ejemplo a continuación muestra este comportamiento.
IMPORTANTE: Tenga en cuenta que la conversión se produce cuando se interpreta el literal de cadena, que es antes de que se almacene en la columna. Esto significa que incluso si la columna puede contener ese carácter, ya se habrá convertido en otra cosa, en función de la Clasificación predeterminada de la Base de datos, todo debido a que omite el N
prefijo en ese literal de cadena. Y esto es exactamente lo que estás (o estabas) experimentando.
Por ejemplo, si la clasificación predeterminada de su base de datos hubiera sido una de las clasificaciones coreanas (uno de los cuatro conjuntos de caracteres de doble byte), entonces no habría visto este problema ya que el carácter "Subíndice 2" está disponible en ese carácter conjunto (página de códigos 949). Pruebe la siguiente prueba para ver (utiliza la Clasificación de la columna en lugar de la Clasificación predeterminada de la base de datos, ya que es más fácil de mostrar):
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;
Devoluciones:
8bit_Latin1_General-1252 8bit_Korean-949 UTF16LE_Latin1_General-1252
2 ₂ ₂
Como puede ver, las colaciones Latin1_General, que usan la página de códigos 1252 (la misma página de códigos que Modern_Spanish
usan las colaciones) para los VARCHAR
datos, no tienen una coincidencia exacta, pero tienen una asignación de "mejor ajuste" (que es lo que está viendo) ) PERO, las colaciones coreanas, que usan la página de códigos 949 para VARCHAR
datos, tienen una coincidencia exacta para el carácter "Subíndice 2".
Para ilustrarlo más, podemos crear una nueva Base de datos con una Clasificación predeterminada de una de las Clasificaciones coreanas, y luego ejecutar el SQL exacto que está en la pregunta:
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;
Devoluciones:
id description
1 CO2
id description
1 CO₂
ACTUALIZAR
Para cualquiera que esté interesado en obtener más información sobre lo que está sucediendo exactamente aquí (es decir, todos los detalles sangrientos), consulte la investigación en dos partes que acabo de publicar: