web-dev-qa-db-fra.com

Comment créer des noms de paramètres et de variables Unicode

Tout cela fonctionne:

CREATE DATABASE [¯\_(ツ)_/¯];
GO
USE [¯\_(ツ)_/¯];
GO
CREATE SCHEMA [¯\_(ツ)_/¯];
GO
CREATE TABLE [¯\_(ツ)_/¯].[¯\_(ツ)_/¯]([¯\_(ツ)_/¯] NVARCHAR(20));
GO
CREATE UNIQUE CLUSTERED INDEX [¯\_(ツ)_/¯] ON [¯\_(ツ)_/¯].[¯\_(ツ)_/¯]([¯\_(ツ)_/¯]);
GO
INSERT INTO [¯\_(ツ)_/¯].[¯\_(ツ)_/¯]([¯\_(ツ)_/¯]) VALUES (N'[¯\_(ツ)_/¯]');
GO
CREATE VIEW [¯\_(ツ)_/¯].[vw_¯\_(ツ)_/¯] AS SELECT [¯\_(ツ)_/¯] FROM [¯\_(ツ)_/¯].[¯\_(ツ)_/¯];
GO
CREATE PROC [¯\_(ツ)_/¯].[sp_¯\_(ツ)_/¯] @Shrug NVARCHAR(20) AS SELECT [¯\_(ツ)_/¯] FROM [¯\_(ツ)_/¯].[vw_¯\_(ツ)_/¯] WHERE [¯\_(ツ)_/¯] = @Shrug;
GO
EXEC [¯\_(ツ)_/¯].[¯\_(ツ)_/¯].[sp_¯\_(ツ)_/¯] @Shrug = N'[¯\_(ツ)_/¯]';
GO

Mais vous pouvez probablement voir où je veux en venir: je ne veux pas de @Shrug, je veux @¯\_(ツ)_/¯.

Aucun de ces travaux sur aucune version de 2008-2017:

CREATE PROC [¯\_(ツ)_/¯].[sp_¯\_(ツ)_/¯] @[¯\_(ツ)_/¯] NVARCHAR(20) AS SELECT [¯\_(ツ)_/¯] FROM [¯\_(ツ)_/¯].[vw_¯\_(ツ)_/¯] WHERE [¯\_(ツ)_/¯] = @[¯\_(ツ)_/¯];
GO
CREATE PROC [¯\_(ツ)_/¯].[sp_¯\_(ツ)_/¯] [@¯\_(ツ)_/¯] NVARCHAR(20) AS SELECT [¯\_(ツ)_/¯] FROM [¯\_(ツ)_/¯].[vw_¯\_(ツ)_/¯] WHERE [¯\_(ツ)_/¯] = [@¯\_(ツ)_/¯];
GO

Alors, existe-t-il un moyen d'utiliser des noms de paramètres de procédure stockée Unicode?

53
Brent Ozar

Eh bien, les identifiants sont toujours Unicode/NVARCHAR, donc techniquement vous ne pouvez pas créer quelque chose qui n'a pas de nom Unicode ????.

Le problème que vous rencontrez ici est entièrement dû à la classification du ou des caractères utilisés. Les règles pour les identificateurs réguliers (c'est-à-dire non délimités) sont:

  • La première lettre doit être:
    • Une lettre telle que définie par la norme Unicode 3.2.
    • trait de soulignement (_), signe (@) ou signe numérique (#)
  • Les lettres suivantes peuvent être:
    • Lettres telles que définies dans la norme Unicode 3.2.
    • Nombres décimaux du latin de base ou d'autres scripts nationaux.
    • souligné (_), au signe (@), au signe numérique (#) ou au signe dollar ($)
  • Les espaces intégrés ou les caractères spéciaux ne sont pas autorisés.
  • Les caractères supplémentaires ne sont pas autorisés.

J'ai mis en gras les seules règles qui importent dans ce contexte. La raison pour laquelle les règles de "première lettre" ne sont pas pertinentes ici est que la première lettre de toutes les variables et paramètres locaux est toujours le "au signe" @.

Et pour être clair: ce qui est considéré comme une "lettre" et ce qui est considéré comme un "chiffre décimal" est basé sur les propriétés que chaque caractère est affecté dans la base de données de caractères Unicode. Unicode attribue de nombreuses propriétés à chaque caractère, telles que: is_uppercase, is_lowercase, is_digit, is_decimal, is_combining, etc., etc. Ces propriétés sont souvent utilisées dans les expressions régulières pour correspondre à la "ponctuation", etc. Par exemple, \p{Lu} Correspond à n'importe quelle lettre majuscule (dans toutes les langues/scripts) et \p{IsDingbats} Correspond à n'importe quelle " Caractère des dingbats.

Donc, dans votre tentative de faire:

DECLARE @¯\_(ツ)_/¯ INT;

seuls les caractères _ (tiret bas ou "ligne basse") et (lettre katakana Tu U + 30C4) entrent dans ces règles. Maintenant, tous les caractères de ¯\_(ツ)_/¯ conviennent pour les identificateurs délimités, mais malheureusement, il semble que les noms de variable/paramètre et les étiquettes GOTO ne peuvent pas être délimités (bien que les noms de curseur puissent l'être).

Donc, pour les noms de variable/paramètre, car ils ne peuvent pas être délimités, vous êtes obligé d'utiliser uniquement des caractères qui sont qualifiés de "lettres" ou de "chiffres décimaux" à partir d'Unicode 3.2 (enfin, selon la documentation; j'ai besoin de tester si les classifications ont été mises à jour pour les versions plus récentes d'Unicode car les classifications sont traitées différemment des poids de tri).

CEPENDANT # 1 , les choses ne sont pas aussi simples qu'elles devraient l'être. J'ai maintenant pu terminer mes recherches et j'ai constaté que la définition indiquée n'est pas entièrement correcte. La définition précise (et vérifiable) des caractères valides pour les identifiants normaux est la suivante:

  • Premier caractère:

    • Peut être n'importe quoi classé dans Unicode 3.2 comme "ID_Start" (qui inclut "Lettres" mais aussi "caractères numériques de type lettre")
    • Peut être _ (Ligne basse/trait de soulignement) ou _ (Ligne basse pleine largeur)
    • Peut être @, Mais seulement pour les variables/paramètres
    • Peut être #, Mais s'il s'agit d'un objet lié au schéma, alors uniquement pour les tables et les procédures stockées (auquel cas ils indiquent que l'objet est temporaire)
  • Caractères suivants:

    • Peut être n'importe quoi classé dans Unicode 3.2 comme "ID_Continue" (qui inclut des nombres "décimaux", mais aussi "des marques de combinaison d'espacement et de non-espacement" et "des signes de ponctuation de connexion"))
    • Peut être @, # Ou $
    • Peut être l'un des 26 caractères classés dans Unicode 3.2 comme caractères de contrôle de format

(fait amusant: "ID" dans "ID_Start" et "ID_Continue" signifie "Identifier". Imaginez cela ;-)

Selon "Unicode Utilities: UnicodeSet":

  • Caractères de départ valides

    [: Age = 3.2:] & [: ID_Start = Oui:]

    -- Test one "Letter" from each of 10+ languages, as of Unicode 3.2
    DECLARE @ᔠᑥᑒᏯשፙᇏᆇᄳᄈლဪඤaൌgೋӁウﺲﶨ   INT;
    -- works
    
    
    -- Test a Supplementary Character that is a "Letter" as of Unicode 3.2
    DECLARE @???? INT;-- Mathematical Script Capital W (U+1D4B2)
    /*
    Msg 102, Level 15, State 1, Line XXXXX
    Incorrect syntax near '0xd835'.
    */
    
  • Caractères de continuation valides

    [: Age = 3.2:] & [: ID_Continue = Oui:]

    -- Test various decimal numbers, but none are Supplementary Characters
    DECLARE @६৮༦൯௫୫9 INT;
    -- works (including some Hebrew and Arabic, which are right-to-left languages)
    
    
    -- Test a Supplementary Character that is a "decimal" number as of Unicode 3.2
    DECLARE @???? INT; -- MATHEMATICAL DOUBLE-STRUCK DIGIT FOUR (U+1D7DC)
    /*
    Msg 102, Level 15, State 1, Line XXXXX
    Incorrect syntax near '0xd835'.
    */
    -- D835 is the first character in the surrogate pair D835 DFDC that makes up U+1D7DC
    

CEPENDANT # 2 , même la recherche dans la base de données Unicode ne peut pas être aussi simple. Ces deux recherches produisent une liste de caractères valides pour ces catégorisations, et ces caractères proviennent d'Unicode 3.2, MAIS les définitions des différentes catégorisations changent dans les versions du standard Unicode. Signification, la définition de "ID_Start" dans Unicode v 10.0 (ce que cette recherche utilise aujourd'hui, 26/03/2018) est pas ce qu'elle était dans Unicode v 3.2. Ainsi, la recherche en ligne ne peut pas fournir une liste exacte. Mais vous pouvez récupérer les fichiers de données Unicode 3.2 et récupérer la liste des caractères "ID_Start" et "ID_Continue" à partir de là pour comparer à ce que SQL Server utilise réellement. Et j'ai fait cela et confirmé une correspondance exacte avec les règles que j'ai énoncées ci-dessus dans "CEPENDANT # 1".

Les deux articles de blog suivants détaillent les étapes suivies pour trouver la liste exacte des personnages, y compris les liens vers les scripts d'importation:

  1. The Uni-Code: la recherche de la vraie liste de caractères valides pour les identifiants réguliers T-SQL, partie 1
  2. L'Uni-Code: la recherche de la vraie liste de caractères valides pour les identifiants réguliers T-SQL, partie 2

Enfin, pour ceux qui veulent juste voir la liste et ne sont pas concernés par ce qu'il a fallu pour la découvrir et la vérifier, vous pouvez le trouver ici:

Liste complète des caractères d'identification T-SQL valides
(veuillez donner un moment à la page pour la charger; c'est 3,5 Mo et près de 47k lignes)


En ce qui concerne "valide" ASCII caractères, tels que / Et -, Ne fonctionne pas: le problème n'a rien à voir avec la définition ou non des caractères dans le jeu de caractères ASCII. Pour être valide, le caractère doit avoir la propriété ID_Start ou ID_Continue, ou être l'une des rares personnalisées les caractères sont notés séparément. Il y a pas mal de caractères "valides" ASCII caractères (62 sur 128 au total - principalement des signes de ponctuation et de contrôle) qui ne sont pas valides dans les identificateurs "réguliers").

En ce qui concerne les caractères supplémentaires: bien qu'ils puissent certainement être utilisés dans des identificateurs délimités (et la documentation ne semble pas indiquer le contraire), s'il est vrai qu'ils ne peuvent pas être utilisés dans des identificateurs réguliers, c'est-à-dire très probablement parce qu'ils ne sont pas entièrement pris en charge dans les fonctions intégrées avant l'introduction des classements de reconnaissance des caractères supplémentaires dans SQL Server 2012 (ils sont traités comme deux caractères "inconnus" individuels), et ils ne peuvent même pas être différenciés les uns des autres dans classements non binaires avant les classements de niveau 100 (introduits dans SQL Server 2008).

Concernant ASCII: les codages 8 bits ne sont pas utilisés ici car tous les identifiants sont Unicode/NVARCHAR/UTF-16 LE. L'instruction SELECT ASCII('ツ'); renvoie une valeur de 63 Qui est un "?" (essayez: SELECT CHAR(63);) puisque ce caractère, même s'il est préfixé par un "N" majuscule, n'est certainement pas dans la page de code 1252. Cependant, ce caractère est dans la page de code coréenne et il produit le bon résultat, même sans le préfixe "N", dans une base de données avec un classement coréen par défaut:

SELECT UNICODE('ツ'); -- 12484

Concernant la première lettre affectant le résultat: ce n'est pas possible car la première lettre des variables et paramètres locaux est toujours @. La première lettre que nous arrivons à contrôler pour ces noms est en fait le 2ème caractère du nom.

En ce qui concerne les raisons pour lesquelles les noms de variables locales, les noms de paramètres et les étiquettes GOTO ne peuvent pas être délimités: je soupçonne que cela est dû au fait que ces éléments font partie du langage lui-même et non quelque chose qui trouvera son chemin dans une table système en tant que données .

44
Solomon Rutzky

Je ne pense pas que c'est Unicode qui cause le problème; dans le cas des noms de variables ou de paramètres locaux, c'est que le caractère n'est pas un caractère ASCII/Unicode 3.2 valide (et il n'y a pas de séquence d'échappement pour les variables/paramètres comme il y en a pour d'autres types d'entités).

Ce lot fonctionne bien, il utilise un caractère Unicode qui ne viole tout simplement pas les règles pour les identificateurs non délimités:

CREATE OR ALTER PROCEDURE dbo.[????]
  @ツ int
AS
  CREATE TABLE [#ツ] (ツ int);
  INSERT [#ツ](ツ) SELECT @ツ;
  SELECT ツ+1 FROM [#ツ];
GO
EXEC dbo.[????] @ツ = 1;

Dès que vous essayez d'utiliser une barre oblique ou un tiret, tous deux valides ASCII caractères, il bombarde:

Msg 102, Level 15, State 1, Procedure ???? Incorrect syntax near '-'.

La documentation n'indique pas pourquoi ces identifiants sont soumis à des règles légèrement différentes de tous les autres identifiants, ni pourquoi ils ne peuvent pas être échappés comme les autres.

22
Aaron Bertrand