La codificación UCS-2 siempre es de 2 bytes por carácter y tiene un rango de 0 - 65535 (0x0000 - 0xFFFF). UTF-16 (independientemente de Big Endian o Little Endian) tiene un rango de 0 - 1114111 (0x0000 - 0x10FFFF). El rango 0 - 65535 / 0x0000 - 0xFFFF de UTF-16 es de 2 bytes por carácter, mientras que el rango superior a 65536 / 0xFFFF es de 4 bytes por carácter.
Windows y SQL Server comenzaron usando la codificación UCS-2 porque estaba disponible y UTF-16 aún no se había finalizado. Afortunadamente, sin embargo, hubo suficiente previsión puesta en los diseños de UCS-2 y UTF-16 para que las asignaciones de UCS-2 sean un subconjunto completo de las asignaciones de UTF-16 (es decir: el rango 0 - 65535 / 0x0000 - 0xFFFF de UTF-16 es UCS-2). Y, el rango 65536-1114111 (0x10000 - 0x10FFFF) de UTF-16 se construye a partir de dos puntos de código en el rango UCS-2 (rangos 0xD800 - 0xDBFF y 0xDC00 - 0xDFFF, específicamente) que se reservaron para este propósito y de lo contrario no tienen sentido. Esta combinación de dos puntos de código se conoce como un par sustituto, y los pares sustitutos representan caracteres más allá del rango UCS-2 que se conocen como caracteres suplementarios.
Toda esa información explica dos aspectos de los NVARCHAR
datos / Unicode en SQL Server:
- Varias funciones integradas (no solo
NCHAR()
) no manejan pares sustitutos / caracteres suplementarios cuando no se utiliza una clasificación complementaria con reconocimiento de caracteres (SCA; es decir, una con _SC
, o _140_
no _BIN*
en el nombre) porque las clasificaciones no SCA (especialmente las SQL_
Las colaciones) se implementaron originalmente antes de que se completara UTF-16 (en algún momento del año 2000, creo). Las no SQL_
intercalaciones que tienen _90_
o _100_
en sus nombres pero no _SC
tienen soporte mínimo para caracteres suplementarios en términos de comparación y clasificación.
- El conjunto completo de caracteres Unicode / UTF-16 se puede almacenar, sin pérdida de datos, en los tipos de datos
NVARCHAR
/ NCHAR
/ XML
/ NTEXT
porque UCS-2 y UTF-16 son exactamente las mismas secuencias de bytes. La única diferencia es que UTF-16 utiliza los puntos de código sustituto para construir pares sustitutos, y UCS-2 simplemente no puede asignarlos a ningún carácter, por lo tanto, aparecen en las funciones integradas como dos caracteres desconocidos.
Con esa información de fondo en mente, ahora podemos pasar por las preguntas específicas:
Me gustaría SELECT NCHAR(128512);
devolver lo mismo que esto:SELECT N'😀';
Eso solo puede suceder si la base de datos actual, donde se ejecuta la consulta, tiene una clasificación predeterminada que es compatible con los caracteres suplementarios, y se introdujeron en SQL Server 2012. Las funciones integradas que tienen parámetros de entrada de cadena pueden tener la clasificación proporcionada en línea a través de la COLLATE
cláusula (es decir LEN(N'string' COLLATE Some_Collation_SC)
) y no es necesario que se ejecute dentro de una base de datos que tenga una clasificación predeterminada de SCA. Sin embargo, las funciones integradas, como NCHAR()
aceptar un INT
parámetro de entrada y la COLLATE
cláusula no son válidas en ese contexto (es por eso que NCHAR()
solo admite caracteres suplementarios cuando la base de datos actual tiene una clasificación predeterminada que es compatible con caracteres suplementarios; pero esto es innecesario inconveniente que se puede cambiar, así que vote por mi sugerencia:La función NCHAR () siempre debe devolver caracteres suplementarios para los valores 0x10000 - 0x10FFFF independientemente de la clasificación predeterminada de la base de datos activa ).
¿Existe una explicación de por qué, independientemente de la intercalación, SQL Server puede comprender y tratar los caracteres extendidos, excepto desde la perspectiva de NCHAR
?
Cómo SQL Server puede almacenar y recuperar caracteres suplementarios sin pérdida de datos se explicó en la sección superior de esta respuesta. Pero no es cierto que NCHAR
sea la única función incorporada que tiene problemas con los caracteres suplementarios (cuando no se utiliza una intercalación SCA). Por ejemplo, LEN(N'😀' COLLATE SQL_Latin1_General_CP1_CI_AS)
devuelve un valor de 2 mientras que LEN(N'😀' COLLATE Latin1_General_100_CI_AS_SC)
devuelve un valor de 1.
Si va al segundo enlace publicado en la Pregunta (es decir, "Información de cotejo de caracteres suplementarios de Microsoft") y se desplaza un poco hacia abajo, verá un gráfico de las funciones integradas y cómo se comportan en función del cotejo efectivo.
¿Cómo encuentro una clasificación que tenga la bandera de "carácter suplementario"?
En una versión de SQL Server anterior a 2012 no puede. Pero, comenzando con SQL Server 2012, puede usar la siguiente consulta:
SELECT col.*
FROM sys.fn_helpcollations() col
WHERE col.[name] LIKE N'%[_]SC'
OR col.[name] LIKE N'%[_]SC[_]%'
OR (COLLATIONPROPERTY(col.[name], 'Version') = 3
AND col.[name] NOT LIKE N'%[_]BIN%');
Su consulta estaba cerca, pero el patrón comenzó SQL
y las Colaciones de SQL Server (es decir, las que comienzan con SQL_
) han quedado en desuso por un tiempo a favor de las Colaciones de Windows (las que no comienzan con SQL_
). Por lo tanto, las SQL_
intercalaciones no se actualizan y, por lo tanto, no tienen versiones más nuevas que incluirían la _SC
opción (y a partir de SQL Server 2017, todas las nuevas intercalaciones admiten automáticamente caracteres suplementarios y no necesitan o tienen el _SC
indicador; y sí, la consulta que se muestra inmediatamente arriba explica eso, así como recoger las _UTF8
intercalaciones agregadas en SQL Server 2019).
¿Se pueden instalar intercalaciones en instancias anteriores?
No, no puede instalar intercalaciones en una versión anterior de SQL Server.
¿Cómo puedo establecer una variable de cadena Unicode (por ejemplo, nvarchar) en un Carácter suplementario usando el código (sin usar el Carácter suplementario real) en una base de datos donde la clasificación "no contiene el indicador de caracteres suplementarios (SC)"?
...
Aunque el servidor es SQL Server 2008 R2, también tengo curiosidad acerca de las soluciones para versiones posteriores.
Cuando no use una Clasificación SCA, puede inyectar Puntos de Código superiores a 65535 / U + FFFF de dos maneras:
- Especifique el par sustituto en términos de dos llamadas a la
NCHAR()
función, cada una con una parte del par
- Especifique el par sustituto en términos de convertir la
VARBINARY
forma de la secuencia de bytes Little Endian (es decir, invertida).
Estos dos métodos para insertar caracteres suplementarios / pares sustitutos funcionarán incluso si la intercalación efectiva es compatible con los caracteres suplementarios, y deberían funcionar igual en todas las versiones de SQL Server, al menos desde 2005 (aunque probablemente también funcionaría en SQL Server 2000 también).
Ejemplo:
- Personaje:
💩
- Nombre: pila de caca
- Decimal: 128169
- Punto de código: U + 1F4A9
- Par sustituto: U + D83D y U + DF21
SELECT N'💩', -- 💩
UNICODE(N'💩' COLLATE Latin1_General_100_CI_AS), -- 55357
UNICODE(N'💩' COLLATE Latin1_General_100_CI_AS_SC), -- 128169
NCHAR(128169), -- 💩 in DB with _SC Collation, else NULL
NCHAR(0x1F4A9), -- 💩 in DB with _SC Collation, else NULL
CONVERT(VARBINARY(4), 128169), -- 0x0001F4A9
CONVERT(VARBINARY(4), N'💩'), -- 0x3DD8A9DC
CONVERT(NVARCHAR(10), 0x3DD8A9DC), -- 💩 (regardless of DB Collation)
NCHAR(0xD83D) + NCHAR(0xDCA9) -- 💩 (regardless of DB Collation)
ACTUALIZAR
Puede usar el siguiente iTVF para obtener los valores de Par sustituto (en ambos INT
y en BINARY
forma) desde cualquier Punto de código entre 65536 - 1114111 (0x010000 - 0x10FFFF). Y, aunque el parámetro de entrada es de tipo INT
, puede pasar en la forma binaria / hexadecimal del Punto de código y se convertirá implícitamente al valor entero correcto.
CREATE FUNCTION dbo.GetSupplementaryCharacterInfo(@CodePoint INT)
RETURNS TABLE
WITH SCHEMABINDING
AS RETURN
WITH calc AS
(
SELECT 55232 + (@CodePoint / 1024) AS [HighSurrogateINT],
56320 + (@CodePoint % 1024) AS [LowSurrogateINT]
WHERE @CodePoint BETWEEN 65536 AND 1114111
)
SELECT @CodePoint AS [CodePointINT],
HighSurrogateINT,
LowSurrogateINT,
CONVERT(VARBINARY(3), @CodePoint) AS [CodePointBIN],
CONVERT(BINARY(2), HighSurrogateINT) AS [HighSurrogateBIN],
CONVERT(BINARY(2), LowSurrogateINT) AS [LowSurrogateBIN],
CONVERT(binary(4), NCHAR(HighSurrogateINT) + NCHAR(LowSurrogateINT)) AS [UTF-16LE],
NCHAR(HighSurrogateINT) + NCHAR(LowSurrogateINT) AS [Character]
FROM calc;
GO
Usando la función anterior, las siguientes dos consultas:
SELECT * FROM dbo.GetSupplementaryCharacterInfo(128169);
SELECT * FROM dbo.GetSupplementaryCharacterInfo(0x01F4A9);
ambos devuelven lo siguiente:
CodePoint HighSurrogate LowSurrgate CodePoint HighSurrgate LowSurrgate UTF-16LE Char
INT INT INT BIN BIN BIN actr
128169 55357 56489 0x01F4A9 0xD83D 0xDCA9 0x3DD8A9DC 💩
ACTUALIZACIÓN 2: ¡Una actualización aún mejor!
He adaptado el iTVF que se muestra arriba para que ahora devuelva 188,657 puntos de código para que no tenga que ajustar ningún valor en particular. Por supuesto, al ser un TVF, puede agregar una WHERE
cláusula para filtrar en un punto de código particular, o rango de puntos de código, o "caracteres similares", etc. Y, incluye columnas adicionales con secuencias de escape preformateadas para construir cada código punto (BMP y caracteres suplementarios) en T-SQL, HTML y estilo C (es decir \xHHHH
). Lea todo sobre esto aquí:
Consejo SSMS # 3: Acceda / investigue fácilmente TODOS los caracteres Unicode (Sí, incluidos los Emojis 😸)