Como definir uma seqüência de caracteres Unicode / NVARCHAR do SQL Server como um emoji ou caractere suplementar?

22

Quero definir uma variável de seqüência de caracteres Unicode para um caractere específico com base em seu ponto de código Unicode.

Desejo usar um ponto de código além de 65535, mas o banco de dados do SQL Server 2008 R2 possui um agrupamento de SQL_Latin1_General_CP1_CI_AS.

De acordo com a documentação NCHAR da Microsoft , a NCHARfunção assume um número inteiro da seguinte maneira:

expressão inteira

Quando o agrupamento do banco de dados não contém o sinalizador de caracteres suplementares (SC), esse é um número inteiro positivo de 0 a 65535 (0 a 0xFFFF). Se um valor fora desse intervalo for especificado, NULL será retornado. Para obter mais informações sobre caracteres suplementares, consulte Suporte a agrupamento e Unicode.

Quando o agrupamento do banco de dados suporta o sinalizador de caracteres suplementares (SC), esse é um número inteiro positivo de 0 a 1114111 (0 a 0x10FFFF). Se um valor fora desse intervalo for especificado, NULL será retornado.

Portanto, este código:

SELECT NCHAR(128512);

Retorna NULLneste banco de dados.

Gostaria que retornasse o mesmo que este:

SELECT N'😀';

Como posso definir uma variável de seqüência de caracteres Unicode (por exemplo, nvarchar) para um emoji usando código (sem usar o caractere emoji real) em um banco de dados em que o agrupamento "não contém o sinalizador de caracteres suplementares (SC)"?

Lista completa de pontos de código Unicode emoji

(Por fim, quero que qualquer personagem funcione. Acabei de escolher emoji para facilitar a referência.)

(Embora o servidor seja o SQL Server 2008 R2, também estou curioso sobre quaisquer soluções para versões posteriores.)

Supondo que não há como, eu poderia referenciar uma função definida pelo usuário em linha em outro banco de dados que tivesse um agrupamento apropriado?

Como encontro um agrupamento com o sinalizador "caráter suplementar"?

Isso não retorna registros em nosso servidor:

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

Parece que o SQL Server 2012 foi introduzido, o Latin1_General_100_CI_AS_SCque funcionaria. Você pode instalar agrupamentos em instâncias mais antigas?

Referências de agrupamento:

Existe uma explicação para o motivo pelo qual, independentemente do agrupamento, o SQL Server pode entender e lidar com os caracteres estendidos, exceto na perspectiva de NCHAR?

Riley Major
fonte
Obrigado pela informação adicional abrangente. Não estou mais enfrentando esse problema, mas vou manter essas informações marcadas mentalmente.
Riley Major
1
Sem problemas. Eu não pensei que você ainda estivesse precisando de algo, apenas para poder apreciar / poder fazer uso da adaptação ...
Solomon Rutzky

Respostas:

35

A codificação UCS-2 é sempre de 2 bytes por caractere e tem um intervalo de 0 a 65535 (0x0000 - 0xFFFF). UTF-16 (independentemente de Big Endian ou Little Endian) tem um intervalo de 0 - 1114111 (0x0000 - 0x10FFFF). O intervalo de 0 - 65535 / 0x0000 - 0xFFFF de UTF-16 é de 2 bytes por caractere, enquanto o intervalo acima de 65536 / 0xFFFF é de 4 bytes por caractere.

O Windows e o SQL Server começaram usando a codificação UCS-2 porque estava disponível e o UTF-16 ainda não havia sido finalizado. Felizmente, no entanto, houve bastante previsão nos designs do UCS-2 e UTF-16 para que os mapeamentos UCS-2 sejam um subconjunto completo dos mapeamentos UTF-16 (o que significa: o intervalo 0 - 65535 / 0x0000 - 0xFFFF de UTF-16 é UCS-2). AND, o intervalo 65536 - 1114111 (0x10000 - 0x10FFFF) do UTF-16 é construído a partir de dois pontos de código no intervalo UCS-2 (intervalos 0xD800 - 0xDBFF e 0xDC00 - 0xDFFF, especificamente) que foram reservados para esse fim e, caso contrário, não possuem significado. Essa combinação de dois Pontos de Código é conhecida como Par Substituto, e Pares Substitutos representam caracteres além do intervalo UCS-2, conhecidos como Caracteres Suplementares.

Todas essas informações explicam dois aspectos dos NVARCHARdados / Unicode no SQL Server:

  1. Várias funções internas (não apenas NCHAR()) não manuseiam pares substitutos / caracteres suplementares quando não se usa um caractere de Ciente Agrupamento Complementar (SCA; isto é, um com _SC, ou _140_ , mas não _BIN*no nome), porque o não-SCA agrupamentos (especialmente o SQL_Collations) foram originalmente implementados antes da conclusão da UTF-16 (em algum momento de 2000, acredito). Os não SQL_agrupamentos que possuem _90_ou _100_em seus nomes, mas não _SCtêm suporte mínimo para caracteres suplementares em termos de comparação e classificação.
  2. O conjunto completo de caracteres Unicode / UTF-16 pode ser armazenado, sem qualquer perda de dados, no NVARCHAR/ NCHAR/ XML/ NTEXTtipos de dados porque UCS-2 e UTF-16 são as mesmas sequências exacto de bytes. A única diferença é que o UTF-16 utiliza os pontos de código substitutos para construir pares substitutos, e o UCS-2 simplesmente não pode mapeá-los para nenhum caractere; portanto, eles aparecem nas funções internas como dois caracteres desconhecidos.

Com essas informações em segundo plano, agora podemos passar por perguntas específicas:

Gostaria SELECT NCHAR(128512);de retornar o mesmo que este:SELECT N'😀';

Isso só pode acontecer se o banco de dados atual - onde a consulta está sendo executada - tiver um agrupamento padrão que seja sensível a caracteres suplementares e que tenha sido introduzido no SQL Server 2012. Funções internas que possuem parâmetros de entrada de sequência podem ter o agrupamento fornecido inline por meio da COLLATEcláusula (ie LEN(N'string' COLLATE Some_Collation_SC)) e não precisa ser executada em um banco de dados que tenha um agrupamento padrão de SCA. No entanto, funções internas como NCHAR()aceitar um INTparâmetro de entrada e a COLLATEcláusula não são válidas nesse contexto (é por isso que NCHAR()apenas suporta caracteres suplementares quando o banco de dados atual possui um agrupamento padrão que é sensível a caracteres suplementares; mas isso é desnecessário inconveniência que pode ser alterada, por favor vote na minha sugestão:A função NCHAR () sempre deve retornar Caractere Suplementar para os valores 0x10000 - 0x10FFFF, independentemente do agrupamento padrão do banco de dados ativo ).

Existe uma explicação para o motivo pelo qual, independentemente do agrupamento, o SQL Server pode entender e lidar com os caracteres estendidos, exceto na perspectiva de NCHAR?

Como o SQL Server pode armazenar e recuperar caracteres suplementares sem perda de dados foi explicado na seção superior desta resposta. Mas, não é verdade que essa NCHARseja a única função interna que possui problemas com caracteres suplementares (quando não estiver usando um agrupamento SCA). Por exemplo, LEN(N'😀' COLLATE SQL_Latin1_General_CP1_CI_AS)retorna um valor de 2 enquanto LEN(N'😀' COLLATE Latin1_General_100_CI_AS_SC)retorna um valor de 1.

Se você acessar o segundo link publicado na pergunta (por exemplo, "Informações adicionais sobre o agrupamento de caracteres da Microsoft") e rolar um pouco para baixo, verá um gráfico das funções internas e como elas se comportam com base no agrupamento efetivo.

Como encontro um agrupamento com o sinalizador "caráter suplementar"?

Em uma versão do SQL Server anterior a 2012, você não pode. Mas, começando no SQL Server 2012, você pode usar a seguinte 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%');

Sua consulta foi encerrada, mas o padrão foi iniciado SQLe os agrupamentos do SQL Server (ou seja, aqueles que começaram com SQL_) foram descontinuados por um tempo a favor dos agrupamentos do Windows (aqueles que não começaram SQL_). Portanto, os SQL_agrupamentos não estão sendo atualizados e, portanto, não têm versões mais recentes que incluam a _SCopção (e, a partir do SQL Server 2017, todos os novos agrupamentos suportam automaticamente caracteres suplementares e não precisam ou têm o _SCsinalizador; e sim, a consulta mostrado imediatamente acima é responsável por isso, além de coletar os _UTF8agrupamentos adicionados no SQL Server 2019).

Você pode instalar agrupamentos em instâncias mais antigas?

Não, você não pode instalar agrupamentos em uma versão anterior do SQL Server.

Como definir uma variável de seqüência de caracteres Unicode (por exemplo, nvarchar) como um caractere suplementar usando o código (sem usar o caractere suplementar real) em um banco de dados em que o agrupamento "não contém o sinalizador de caractere complementar (SC)"?
...
Embora o servidor seja o SQL Server 2008 R2, também estou curioso sobre quaisquer soluções para versões posteriores.

Quando não estiver usando um agrupamento SCA, é possível injetar pontos de código acima de 65535 / U + FFFF de duas maneiras:

  1. Especifique o par substituto em termos de duas chamadas para a NCHAR()função, cada uma com uma parte do par
  2. Especifique o Par Substituto em termos de conversão da VARBINARYforma da sequência de bytes Little Endian (ou seja, invertida).

Esses dois métodos de inserção de pares de caracteres suplementares / substitutos funcionarão mesmo que o agrupamento efetivo seja sensível a caracteres suplementares e devem funcionar da mesma forma em todas as versões do SQL Server, pelo menos desde 2005 (embora provavelmente também funcione em SQL Server 2000 também).

Exemplo:

  • Personagem:

                       💩

  • Nome:                Pilha de Poo
  • Decimal:            128169
  • Ponto de código:       U + 1F4A9
  • Par substituto: U + D83D e 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)

ATUALIZAR

Você pode usar o seguinte iTVF para obter os valores do Par Substituto (em ambos INTe na BINARYforma) de qualquer Ponto de Código entre 65536 - 1114111 (0x010000 - 0x10FFFF). E, embora o parâmetro de entrada seja do tipo INT, você pode passar na forma binária / hexadecimal do ponto de código e ele implicitamente será convertido no valor inteiro correto.

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 a função acima, as duas consultas a seguir:

SELECT * FROM dbo.GetSupplementaryCharacterInfo(128169);

SELECT * FROM dbo.GetSupplementaryCharacterInfo(0x01F4A9);

ambos retornam o seguinte:

CodePoint  HighSurrogate  LowSurrgate  CodePoint  HighSurrgate  LowSurrgate  UTF-16LE   Char
INT        INT            INT          BIN        BIN           BIN                     actr
128169     55357          56489        0x01F4A9   0xD83D        0xDCA9       0x3DD8A9DC   💩

ATUALIZAÇÃO 2: Uma atualização ainda melhor!

Eu adaptei o iTVF mostrado acima para retornar agora 188.657 pontos de código, para que você não precise ajustar nenhum valor específico. É claro que, sendo um TVF, você pode adicionar uma WHEREcláusula para filtrar um ponto de código específico, ou intervalo de pontos de código ou "caracteres semelhantes" etc. etc. E inclui colunas adicionais com seqüências de escape pré-formatadas para construir cada código ponto (BMP e caracteres suplementares) no estilo T-SQL, HTML e C (ou seja \xHHHH). Leia tudo sobre isso aqui:

Dica 3 do SSMS: acesse / pesquise facilmente todos os caracteres Unicode (sim, incluindo emojis)

Solomon Rutzky
fonte
1
Bom trabalho, Salomão! Explicação impressionante
Ronen Ariely 16/01