¿Cómo configuro una cadena Unicode / NVARCHAR de SQL Server en un emoji o carácter suplementario?

23

Quiero establecer una variable de cadena Unicode en un carácter particular en función de su punto de código Unicode.

Quiero usar un punto de código más allá de 65535, pero la base de datos SQL Server 2008 R2 tiene una clasificación de SQL_Latin1_General_CP1_CI_AS.

Según la documentación de NCHAR de Microsoft , la NCHARfunción toma un número entero de la siguiente manera:

entero_expresión

Cuando la clasificación de la base de datos no contiene el indicador de caracteres suplementarios (SC), este es un número entero positivo de 0 a 65535 (0 a 0xFFFF). Si se especifica un valor fuera de este rango, se devuelve NULL. Para obtener más información sobre los caracteres adicionales, consulte Compatibilidad y compatibilidad con Unicode.

Cuando la clasificación de la base de datos admite el indicador de caracteres suplementarios (SC), este es un número entero positivo de 0 a 1114111 (0 a 0x10FFFF). Si se especifica un valor fuera de este rango, se devuelve NULL.

Entonces este código:

SELECT NCHAR(128512);

Devoluciones NULLen esta base de datos.

Me gustaría que devuelva lo mismo que esto:

SELECT N'😀';

¿Cómo puedo establecer una variable de cadena Unicode (por ejemplo, nvarchar) en un emoji usando el código (sin usar el carácter emoji real) en una base de datos donde la clasificación "no contiene el indicador de caracteres suplementarios (SC)"?

Lista completa de puntos de código emoji Unicode

(En última instancia, quiero que funcione cualquier personaje. Simplemente elegí emoji para facilitar la referencia).

(Aunque el servidor es SQL Server 2008 R2, también tengo curiosidad sobre las soluciones para versiones posteriores).

Suponiendo que no hay forma, ¿podría hacer referencia a una función definida por el usuario en línea en otra base de datos que tuviera una clasificación adecuada?

¿Cómo encuentro una clasificación que tenga la bandera de "carácter suplementario"?

Esto no devuelve ningún registro en nuestro servidor:

SELECT * FROM sys.fn_helpcollations() 
WHERE name LIKE 'SQL%[_]SC';

Parece Latin1_General_100_CI_AS_SCque se introdujo SQL Server 2012, que funcionaría. ¿Se pueden instalar intercalaciones en instancias anteriores?

Recopilación de referencias:

¿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?

Riley Major
fuente
Gracias por la información adicional completa. Ya no me enfrento a este problema, pero mantendré esta información marcada mentalmente.
Riley Major
1
No hay problema. No pensé que todavía necesitabas algo, solo para que pudieras apreciar / poder hacer uso de la adaptación ...
Solomon Rutzky

Respuestas:

36

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 NVARCHARdatos / Unicode en SQL Server:

  1. 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 _SCtienen soporte mínimo para caracteres suplementarios en términos de comparación y clasificación.
  2. El conjunto completo de caracteres Unicode / UTF-16 se puede almacenar, sin pérdida de datos, en los tipos de datos NVARCHAR/ NCHAR/ XML/ NTEXTporque 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 COLLATEclá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 INTparámetro de entrada y la COLLATEclá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 NCHARsea ​​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ó SQLy 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 _SCopción (y a partir de SQL Server 2017, todas las nuevas intercalaciones admiten automáticamente caracteres suplementarios y no necesitan o tienen el _SCindicador; y sí, la consulta que se muestra inmediatamente arriba explica eso, así como recoger las _UTF8intercalaciones 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:

  1. Especifique el par sustituto en términos de dos llamadas a la NCHAR()función, cada una con una parte del par
  2. Especifique el par sustituto en términos de convertir la VARBINARYforma 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 INTy en BINARYforma) 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 WHEREclá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 😸)

Solomon Rutzky
fuente
1
¡Buen trabajo, Salomón! Impresionante explicación
Ronen Ariely