Come creare nomi di parametri e variabili Unicode


53

Tutto questo funziona:

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

Ma probabilmente puoi vedere dove sto andando con questo: non voglio @Shrug, voglio @¯\_(ツ)_/¯.

Nessuno di questi funziona su nessuna versione dal 2008-2017:

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

Quindi, c'è un modo per usare i nomi dei parametri di stored procedure unicode?

Risposte:


44

Bene, gli identificatori sono sempre Unicode / NVARCHAR, quindi tecnicamente non puoi creare nulla che non abbia un nome Unicode 🙃.

Il problema che stai riscontrando qui è dovuto interamente alla classificazione del personaggio (i) in uso. Le regole per gli identificatori regolari (cioè non delimitati) sono:

  • La prima lettera deve essere:
    • Una lettera come definita dallo standard Unicode 3.2.
    • trattino basso (_), al segno (@) o segno numerico (#)
  • Le lettere successive possono essere:
    • Lettere come definito nello standard Unicode 3.2.
    • Numeri decimali dal latino di base o da altri script nazionali.
    • trattino basso (_), al segno (@), segno numerico (#) o segno del dollaro ($)
  • Non sono ammessi spazi incorporati o caratteri speciali.
  • Non sono ammessi caratteri supplementari.

Ho messo in grassetto le uniche regole che contano in questo contesto. La ragione per cui le regole della "Prima lettera" non sono rilevanti qui è che la prima lettera in tutte le variabili e i parametri locali è sempre "al segno" @.

E per essere chiari: ciò che è considerato una "lettera" e ciò che è considerato una "cifra decimale" si basa sulle proprietà assegnate a ciascun carattere nel database dei caratteri Unicode. Unicode assegna molte proprietà a ciascun carattere, come ad esempio: is_uppercase, is_lowercase, is_digit, is_decimal, is_combining, ecc. Ecc. Queste proprietà vengono spesso utilizzate nelle espressioni regolari per corrispondere alla "punteggiatura", ecc. Ad esempio, \p{Lu}corrisponde a qualsiasi lettera maiuscola (in tutte le lingue / script) e \p{IsDingbats}corrisponde a qualsiasi carattere "Simboli".

Quindi, nel tuo tentativo di fare:

DECLARE @¯\_(ツ)_ INT;

solo i caratteri _(trattino basso o "linea bassa") e (Katakana Letter Tu U + 30C4) rientrano in quelle regole. Ora, tutti i caratteri in ¯\_(ツ)_/¯vanno bene per gli identificatori delimitati, ma sfortunatamente sembra che i nomi e le GOTOetichette di variabili / parametri non possano essere delimitati (sebbene i nomi dei cursori possano esserlo).

Quindi, per i nomi di variabili / parametri, poiché non possono essere delimitati, si è bloccati nell'utilizzare solo caratteri che si qualificano come "lettere" o "cifre decimali" a partire da Unicode 3.2 (bene, secondo la documentazione; ho bisogno di testare se le classificazioni sono state aggiornate per le versioni più recenti di Unicode poiché le classificazioni sono gestite in modo diverso rispetto ai pesi di ordinamento).

TUTTAVIA # 1 , le cose non sono così semplici come dovrebbero essere. Sono stato ora in grado di completare la mia ricerca e ho scoperto che la definizione dichiarata non è del tutto corretta. La definizione precisa (e verificabile) di quali caratteri sono validi per identificatori regolari è:

  • Primo personaggio:

    • Può essere qualsiasi cosa classificata in Unicode 3.2 come "ID_Start" (che include "Lettere" ma anche "caratteri numerici simili a lettere")
    • Può essere _(linea bassa / trattino basso) o _(linea bassa larghezza intera)
    • Può essere @, ma solo per variabili / parametri
    • Può essere #, ma se oggetto associato a schema, solo per tabelle e stored procedure (nel qual caso indicano che l'oggetto è temporaneo)
  • Personaggi successivi:

    • Può essere qualsiasi cosa classificata in Unicode 3.2 come "ID_Continua" (che include numeri "decimali", ma anche "segni di combinazione spaziatura e non spaziatura" e "collegamento di segni di punteggiatura")
    • Può essere @, #o$
    • Può essere uno qualsiasi dei 26 caratteri classificati in Unicode 3.2 come caratteri di controllo del formato

(fatto divertente: "ID" in "ID_Start" e "ID_Continue" sta per "Identificatore". Immagina che ;-)

Secondo "Unicode Utilities: UnicodeSet":

  • Caratteri iniziali validi

    [: Età = 3.2:] & [: ID_Start = Sì:]

    -- Test one "Letter" from each of 10+ languages, as of Unicode 3.2
    DECLARE @ᔠᑥᑒᏯשፙᇏᆇᄳᄈლဪඤagೋӁウﺲﶨ   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'.
    */
  • Caratteri di continuazione validi

    [: Età = 3.2:] & [: ID_Continue = Sì:]

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

TUTTAVIA # 2 , nemmeno la ricerca nel database Unicode può essere così facile. Queste due ricerche producono un elenco di caratteri validi per tali categorizzazioni e quei caratteri provengono da Unicode 3.2, MA le definizioni delle varie modifiche alle categorizzazioni attraverso le versioni dello standard Unicode. Significato, la definizione di "ID_Start" in Unicode v 10.0 (ciò che quella ricerca sta usando oggi, 26-03-2018) non è quella che era in Unicode v 3.2. Pertanto, la ricerca online non può fornire un elenco esatto. Ma puoi prendere i file di dati Unicode 3.2 e prendere la lista dei caratteri "ID_Start" e "ID_Continue" da lì per confrontare con ciò che SQL Server effettivamente utilizza. E ho fatto questo e confermato una corrispondenza esatta con le regole che ho affermato sopra in "TUTTAVIA # 1".

I seguenti due post del blog descrivono in dettaglio i passaggi da seguire per trovare l'elenco esatto dei caratteri, inclusi i collegamenti agli script di importazione:

  1. Il codice Uni: la ricerca del vero elenco di caratteri validi per identificatori regolari T-SQL, parte 1
  2. Il codice Uni: la ricerca del vero elenco di caratteri validi per identificatori regolari T-SQL, parte 2

Infine, per chiunque voglia solo vedere l'elenco e non si preoccupa di ciò che è stato necessario per scoprirlo e verificarlo, lo puoi trovare qui:

Elenco completamente completo di caratteri identificativi T-SQL validi
(si prega di dare alla pagina un momento per caricare; sono 3,5 MB e quasi 47k righe)


Per quanto riguarda i caratteri ASCII "validi", come /e -, non funzionanti: il problema non ha nulla a che vedere con la definizione o meno dei caratteri nel set di caratteri ASCII. Per essere valido, il personaggio deve avere la proprietà ID_Starto ID_Continueoppure deve essere uno dei pochi caratteri personalizzati annotati separatamente. Esistono alcuni caratteri ASCII "validi" (62 dei 128 totali - principalmente caratteri di punteggiatura e di controllo) che non sono validi negli identificatori "regolari".

Per quanto riguarda i caratteri supplementari: sebbene possano certamente essere utilizzati in identificatori delimitati (e la documentazione non sembra indicare diversamente), se è vero che non possono essere utilizzati in identificatori regolari, è molto probabile che non siano pienamente supportati nelle funzioni incorporate prima delle regole di confronto con riconoscimento dei caratteri supplementari sono state introdotte in SQL Server 2012 (vengono trattate come due singoli caratteri "sconosciuti"), né potrebbero essere differenziate tra loro in regole di confronto non binarie prima del 100- Collazioni di livello (introdotte in SQL Server 2008).

Per quanto riguarda ASCII: le codifiche a 8 bit non vengono utilizzate qui poiché tutti gli identificatori sono Unicode / NVARCHAR/ UTF-16 LE. L'istruzione SELECT ASCII('ツ');restituisce un valore di 63cui è un "?" (prova:) SELECT CHAR(63);dal momento che quel carattere, anche se preceduto da una "N" maiuscola, non è certamente nel Codice Pagina 1252. Tuttavia, quel carattere è nel Codice pagina coreano e produce il risultato corretto, anche senza la "N" "prefisso, in un database con regole di confronto predefinite coreane:

SELECT UNICODE('ツ'); -- 12484

Per quanto riguarda la prima lettera che influisce sul risultato: ciò non è possibile poiché la prima lettera per variabili e parametri locali è sempre @. La prima lettera che possiamo controllare per questi nomi è in realtà il 2 ° carattere del nome.

Per quanto riguarda il motivo per cui i nomi delle variabili locali, i nomi dei parametri e le GOTOetichette non possono essere delimitati: sospetto che ciò sia dovuto al fatto che questi elementi fanno parte del linguaggio stesso e non qualcosa che troverà la sua strada in una tabella di sistema come dati.


Fantastico, grazie. Questo mi ha portato a questo, che sarà un grande post sul blog: gist.github.com/BrentOzar/9b08b5ab2b617847dbe4aa0297b4cd5b
Brent Ozar

8
@BrentOzar hai avuto una TAC di recente?
Ross Presser,

Caspita, questa è una risposta davvero impressionante! E secondo il commento di Ross Presser.
SQL Nerd,

22

Non penso che sia Unicode a causare il problema; nel caso di nomi di variabili o parametri locali, è che il carattere non è un carattere ASCII / Unicode 3.2 valido (e non esiste alcuna sequenza di escape per variabili / parametri come per altri tipi di entità).

Questo batch funziona bene, utilizza un carattere Unicode che semplicemente non viola le regole per identificatori non delimitati:

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

Non appena si tenta di utilizzare una barra o un trattino, entrambi caratteri ASCII validi, bombe:

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

La documentazione non affronta il motivo per cui questi identificatori sono soggetti a regole leggermente diverse rispetto a tutti gli altri identificatori o perché non possono essere sfuggiti come gli altri.


Ciao Aaron. Giusto per chiarire alcuni punti qui: 1) il primo personaggio non è un problema poiché il primo carattere è in realtà il @nome var / param. Tutti i personaggi che non funzionano non dovrebbero funzionare in nessuna posizione, anche se preceduti da caratteri validi. 2) il documento afferma solo che i caratteri supplementari non possono essere utilizzati negli identificatori regolari (il che sembra essere il caso di tutto ciò che ho provato), ma non pone restrizioni agli identificatori delimitati, proprio come con gli spazi incorporati. Inoltre, credo che questi siano diversi perché fanno parte del linguaggio T-SQL, non delle cose nel DB.
Solomon Rutzky,

@SolomonRutzky Sento che il problema è semplicemente e del tutto che un nome di parametro non può essere delimitato come altre entità. Se potessi racchiudere parentesi quadre o virgolette doppie attorno al nome di un parametro, potrei inserirvi uno di questi caratteri, in qualsiasi posizione. La domanda postula che non è possibile utilizzare i caratteri Unicode nel nome di un parametro, e chiaramente non è così. Ci sono alcuni caratteri Unicode che puoi usare e alcuni caratteri ASCII che non puoi .
Aaron Bertrand

Sì, sono d'accordo che se i nomi delle variabili / dei parametri e le GOTOetichette consentissero di essere delimitati, l'unica limitazione sarebbe la lunghezza. Posso solo supporre che l'analisi e / o la gestione di quei pochi elementi avvenga a un livello diverso o abbia alcuni altri vincoli che hanno reso inattuabili i valori delimitati. Almeno spero che non fosse arbitrario o una svista.
Solomon Rutzky,

(non avevo visto l'aggiornamento al tuo commento quando ho risposto un momento fa). Sì, la domanda implica che l'OP non è in grado di utilizzare i caratteri Unicode, ma il fraseggio della domanda è tecnicamente errato poiché tutti i nomi sono sempre Unicode / NVARCHAR. Questo non ha nulla a che fare con ASCII poiché si tratta di una codifica a 8 bit che non viene utilizzata qui. Tutti i caratteri qui sono caratteri Unicode, anche se alcuni esistono anche in varie pagine di codice a 8 bit. Come ho spiegato nella mia risposta, quali caratteri possono essere usati dipende da quali sono stati taggati con is_alphabetico numeric_type=decimal.
Solomon Rutzky,

Ho visto procs memorizzati che erano pieni di cacca ma non l'ho mai chiamato!
Mitch Wheat,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.