web-dev-qa-db-fra.com

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

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

Je souhaite 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 documentation NCHAR de Microsoft , la fonction NCHAR 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);

Renvoie NULL dans 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 introduit Latin1_General_100_CI_AS_SC 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 traiter les caractères étendus, sauf du point de vue de NCHAR?

25
Riley Major

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 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 données NVARCHAR/Unicode dans SQL Server:

  1. Plusieurs fonctions intégrées (et pas seulement NCHAR()) ne gèrent pas les paires de substitution/caractères supplémentaires lorsque vous n'utilisez pas un classement supplémentaire conscient des caractères (SCA; c'est-à-dire un avec _SC, ou_140_ mais pas _BIN* dans le nom) car les classements non SCA (en particulier les classements SQL_) ont été initialement implémentés avant UTF-16 en cours d'achèvement (en 2000, je crois). Les classements autres que SQL_ Qui ont _90_ Ou _100_ Dans leur nom mais pas _SC Ont une prise en charge minimale des 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/NTEXT car 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 que SELECT NCHAR(128512); renvoie la même chose que ceci: 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 clause COLLATE (c'est-à-dire LEN(N'string' COLLATE Some_Collation_SC)) et ne pas besoin être exécuté dans une base de données qui a un classement SCA par défaut. Cependant, les fonctions intégrées telles que NCHAR() acceptent un paramètre d'entrée INT et la clause COLLATE n'est pas valide 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 c'est un inconvénient inutile qui peut être modifié, veuillez donc voter pour ma suggestion: la fonction NCHAR () devrait toujours renvoyer 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 traiter 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 section supérieure de cette réponse. Mais, il n'est pas vrai que NCHAR 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 SQL et 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 classements SQL_ Ne sont pas mis à jour et n'ont donc pas de versions plus récentes qui incluraient l'option _SC (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'indicateur _SC; et oui, la requête affichée immédiatement ci-dessus en tient compte et récupère les classements _UTF8 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 connaître les 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 fonction NCHAR(), chacun avec une partie de la paire
  2. Spécifiez la paire de substitution en termes de conversion de la forme VARBINARY 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:

  • Caractère:

    ????

  • 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 (sous la forme INT et BINARY) à 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 clause WHERE pour filtrer sur un point de code particulier, ou une plage de points de code, ou des "caractères similaires", etc. Et, il comprend des colonnes supplémentaires avec des formats pré-formatés séquences d'échappement pour construire chaque point de code (à la fois BMP et caractères supplémentaires) dans T-SQL (sans nécessiter un classement "_SC" ou "_140_") , HTML (et XML), le style commun à de nombreux langages d'application ("\ uHHHH"; utilisé pour C++/C #/F #/Java/JavaScript/Julia/etc), et enfin légèrement plus récent, un autre style commun qui gère tous les points de code, pas seulement BMP ("\ UHHHHHHHH"; utilisé pour C/C++/C #/F #/Julia/etc).

Lisez tout a propos de ça ici:

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

40
Solomon Rutzky