Comment définir une chaîne SQL Server Unicode / NVARCHAR sur un emoji ou un caractère supplémentaire?

23

Je veux définir une variable de chaîne Unicode sur un caractère particulier en fonction de son point de code Unicode.

Je veux utiliser un point de code au-delà de 65535, mais la base de données SQL Server 2008 R2 a un classement de SQL_Latin1_General_CP1_CI_AS.

Selon la documentation NCHAR de Microsoft , la NCHARfonction prend un entier comme suit:

expression_entier

Lorsque le classement de la base de données ne contient pas l'indicateur de caractère supplémentaire (SC), il s'agit d'un nombre entier positif compris entre 0 et 65535 (0 et 0xFFFF). Si une valeur en dehors de cette plage est spécifiée, NULL est retourné. Pour plus d'informations sur les caractères supplémentaires, voir Prise en charge du classement et d'Unicode.

Lorsque le classement de la base de données prend en charge l'indicateur de caractère supplémentaire (SC), il s'agit d'un nombre entier positif compris entre 0 et 1114111 (0 et 0x10FFFF). Si une valeur en dehors de cette plage est spécifiée, NULL est retourné.

Donc, ce code:

SELECT NCHAR(128512);

Retourne NULLdans cette base de données.

Je voudrais qu'il renvoie le même que celui-ci:

SELECT N'😀';

Comment puis-je définir une variable de chaîne Unicode (par exemple nvarchar) sur un emoji en utilisant du code (sans utiliser le caractère emoji réel) dans une base de données où le classement "ne contient pas l'indicateur de caractère supplémentaire (SC)"?

Liste complète des points de code emoji Unicode

(En fin de compte, je veux que n'importe quel personnage fonctionne. J'ai simplement choisi les emoji pour faciliter la référence.)

(Bien que le serveur soit SQL Server 2008 R2, je suis également curieux de savoir quelles solutions pour les versions ultérieures.)

En supposant qu'il n'y ait aucun moyen, pourrais-je référencer une fonction définie par l'utilisateur en ligne dans une autre base de données qui avait un classement approprié?

Comment trouver un classement qui a le drapeau "caractère supplémentaire"?

Cela ne renvoie aucun enregistrement sur notre serveur:

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

Il semble que SQL Server 2012 a été introduit, Latin1_General_100_CI_AS_SCce qui fonctionnerait. Pouvez-vous installer des classements sur des instances plus anciennes?

Références de collation:

Y a-t-il une explication pour laquelle, indépendamment du classement, SQL Server peut comprendre et gérer les caractères étendus, sauf du point de vue de NCHAR?

Riley Major
la source
Merci pour les informations supplémentaires complètes. Je ne suis plus confronté à ce problème, mais je garderai cette information mentalement en signet.
Riley Major
1
Aucun problème. Je ne pensais pas que vous aviez encore besoin de quelque chose, juste que vous pourriez apprécier / pouvoir utiliser l'adaptation ...
Solomon Rutzky

Réponses:

36

Le codage UCS-2 est toujours de 2 octets par caractère et a une plage de 0 à 65535 (0x0000 - 0xFFFF). UTF-16 (indépendamment de Big Endian ou Little Endian) a une plage de 0 - 1114111 (0x0000 - 0x10FFFF). La plage 0 - 65535 / 0x0000 - 0xFFFF de UTF-16 est de 2 octets par caractère tandis que la plage au-dessus de 65536 / 0xFFFF est de 4 octets par caractère.

Windows et SQL Server ont commencé à utiliser l'encodage UCS-2 car il était disponible et UTF-16 n'était pas encore finalisé. Heureusement, cependant, les conceptions de UCS-2 et UTF-16 ont été suffisamment conçues pour que les mappages UCS-2 soient un sous-ensemble complet des mappages UTF-16 (ce qui signifie: la plage 0 - 65535 / 0x0000 - 0xFFFF de l'UTF-16 est UCS-2). ET, la plage UTF-16 65536 - 1114111 (0x10000 - 0x10FFFF) est construite à partir de deux points de code dans la plage UCS-2 (plages 0xD800 - 0xDBFF et 0xDC00 - 0xDFFF, en particulier) qui ont été réservés à cet effet et qui n'ont autrement aucune sens. Cette combinaison de deux points de code est connue sous le nom de paire de substitution, et les paires de substitution représentent des caractères au-delà de la plage UCS-2, appelés caractères supplémentaires.

Toutes ces informations expliquent deux aspects des NVARCHARdonnées / Unicode dans SQL Server:

  1. Plusieurs fonctions intégrées (non seulement NCHAR()) ne gèrent pas / paires Surrogate caractères supplémentaires lorsqu'ils ne sont pas en utilisant un caractère supplémentaire-Aware Collation (SCA, à savoir un avec _SC, ou _140_ non _BIN*dans le nom) parce que le (non-SCA en particulier la collation par défaut SQL_Les classements) ont été mis en œuvre à l'origine avant l'achèvement de l'UTF-16 (quelque part en 2000, je crois). Les non- SQL_classements qui ont _90_ou _100_en leur nom mais ne _SCprennent pas en charge les caractères supplémentaires en termes de comparaison et de tri.
  2. Le jeu de caractères Unicode / UTF-16 complet peut être stocké, sans aucune perte de données, dans les types de données NVARCHAR/ NCHAR/ XML/ NTEXTcar UCS-2 et UTF-16 sont exactement les mêmes séquences d'octets. La seule différence est que UTF-16 utilise les points de code de substitution pour construire des paires de substitution, et UCS-2 ne peut tout simplement pas les mapper à des caractères, ils apparaissent donc dans les fonctions intégrées comme deux caractères inconnus.

Avec ces informations de fond à l'esprit, nous pouvons maintenant passer par les questions spécifiques:

Je voudrais SELECT NCHAR(128512);retourner le même que celui-ci:SELECT N'😀';

Cela ne peut se produire que si la base de données actuelle - où la requête est exécutée - a un classement par défaut qui est compatible avec les caractères supplémentaires, et ceux qui ont été introduits dans SQL Server 2012. Les fonctions intégrées qui ont des paramètres d'entrée de chaîne peuvent avoir le classement fourni en ligne via la COLLATEclause (c'est-à-dire LEN(N'string' COLLATE Some_Collation_SC)) et n'ont pas besoin d'être exécutés dans une base de données qui a un classement SCA par défaut. Cependant, les fonctions intégrées telles que l' NCHAR()acceptation d'un INTparamètre d'entrée et la COLLATEclause ne sont pas valides dans ce contexte (c'est pourquoi NCHAR()ne prend en charge les caractères supplémentaires que lorsque la base de données actuelle a un classement par défaut qui est compatible avec les caractères supplémentaires; mais ceci n'est pas nécessaire inconvénients qui peuvent être modifiés, veuillez donc voter pour ma suggestion:La fonction NCHAR () doit toujours renvoyer un caractère supplémentaire pour les valeurs 0x10000 - 0x10FFFF, indépendamment du classement par défaut de la base de données active ).

Y a-t-il une explication pour laquelle, indépendamment du classement, SQL Server peut comprendre et gérer les caractères étendus, sauf du point de vue de NCHAR?

La façon dont SQL Server peut stocker et récupérer des caractères supplémentaires sans perte de données a été expliquée dans la partie supérieure de cette réponse. Mais, il n'est pas vrai que NCHARc'est la seule fonction intégrée qui a des problèmes avec les caractères supplémentaires (lorsque vous n'utilisez pas un classement SCA). Par exemple, LEN(N'😀' COLLATE SQL_Latin1_General_CP1_CI_AS)renvoie une valeur de 2 tandis que LEN(N'😀' COLLATE Latin1_General_100_CI_AS_SC)renvoie une valeur de 1.

Si vous accédez au deuxième lien publié dans la question (c'est-à-dire «Informations de classement des caractères supplémentaires de Microsoft») et faites défiler un peu vers le bas, vous verrez un graphique des fonctions intégrées et comment elles se comportent en fonction du classement effectif.

Comment trouver un classement qui a le drapeau "caractère supplémentaire"?

Dans une version de SQL Server antérieure à 2012, vous ne pouvez pas. Mais, à partir de SQL Server 2012, vous pouvez utiliser la requête suivante:

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%');

Votre requête était fermée, mais le modèle a commencé par SQLet les classements SQL Server (c'est-à-dire ceux commençant par SQL_) sont obsolètes depuis un certain temps en faveur des classements Windows (ceux qui ne commencent pas par SQL_). Ainsi, les SQL_classements ne sont pas mis à jour et n'ont donc pas de versions plus récentes qui incluraient l' _SCoption (et à partir de SQL Server 2017, tous les nouveaux classements prennent automatiquement en charge les caractères supplémentaires et n'ont pas besoin, ou n'ont pas, l' _SCindicateur; et oui, la requête montré juste au-dessus explique cela et reprend les _UTF8classements ajoutés dans SQL Server 2019).

Pouvez-vous installer des classements sur des instances plus anciennes?

Non, vous ne pouvez pas installer Collations dans une version précédente de SQL Server.

Comment puis-je définir une variable de chaîne Unicode (par exemple nvarchar) sur un caractère supplémentaire en utilisant du code (sans utiliser le caractère supplémentaire réel) dans une base de données où le classement "ne contient pas l'indicateur de caractère supplémentaire (SC)"?
...
Bien que le serveur soit SQL Server 2008 R2, je suis également curieux de savoir quelles solutions pour les versions ultérieures.

Lorsque vous n'utilisez pas de classement SCA, vous pouvez injecter des points de code au-dessus de 65535 / U + FFFF de deux manières:

  1. Spécifiez la paire de substitution en termes de deux appels à la NCHAR()fonction, chacun avec une partie de la paire
  2. Spécifiez la paire de substitution en termes de conversion de la VARBINARYforme de la séquence d'octets Little Endian (c'est-à-dire inversée).

Ces deux méthodes d'insertion de caractères supplémentaires / paires de substitution fonctionneront même si le classement effectif est compatible avec les caractères supplémentaires, et devraient fonctionner de la même manière sur toutes les versions de SQL Server, au moins aussi tôt qu'en 2005 (mais cela fonctionnerait probablement aussi dans SQL Server 2000 également).

Exemple:

  • Personnage:

                       💩

  • Nom:                Pile of Poo
  • Décimal:            128169
  • Point de code:       U + 1F4A9
  • Paire de substitution: U + D83D & 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)

MISE À JOUR

Vous pouvez utiliser l'iTVF suivant pour obtenir les valeurs de la paire de substitution (dans les deux INTet BINARYsous forme) à partir de n'importe quel point de code entre 65536 - 1114111 (0x010000 - 0x10FFFF). Et, tandis que le paramètre d'entrée est de type INT, vous pouvez passer sous la forme binaire / hexadécimale du point de code et il sera implicitement converti en la valeur entière correcte.

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

En utilisant la fonction ci-dessus, les deux requêtes suivantes:

SELECT * FROM dbo.GetSupplementaryCharacterInfo(128169);

SELECT * FROM dbo.GetSupplementaryCharacterInfo(0x01F4A9);

les deux renvoient les éléments suivants:

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

MISE À JOUR 2: Une mise à jour encore meilleure!

J'ai adapté l'iTVF illustré ci-dessus pour renvoyer maintenant 188 657 points de code, vous n'avez donc pas besoin de l'adapter à une valeur particulière. Bien sûr, étant un TVF, vous pouvez ajouter une WHEREclause pour filtrer sur un point de code particulier, ou une plage de points de code, ou "caractères similaires", etc. Et, il comprend des colonnes supplémentaires avec des séquences d'échappement pré-formatées pour construire chaque code point (à la fois BMP et caractères supplémentaires) en T-SQL, HTML et style C (c'est-à-dire \xHHHH). Lisez tout a propos de ça ici:

Astuce SSMS # 3: Accédez / recherchez facilement TOUS les caractères Unicode (Oui, y compris les Emojis 😸)

Solomon Rutzky
la source
1
Excellent travail Salomon! Super explication
Ronen Ariely