So erstellen Sie Unicode-Parameter- und Variablennamen

53

All dies funktioniert:

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

Aber Sie können wahrscheinlich sehen, wohin ich gehe: Ich will nicht @Shrug, ich will @¯\_(ツ)_/¯.

Keines dieser Werke funktioniert in irgendeiner Version von 2008-2017:

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

Gibt es eine Möglichkeit, die Parameternamen von gespeicherten Unicode-Prozeduren zu verwenden?

Brent Ozar
quelle

Antworten:

44

Nun, Bezeichner sind immer Unicode / NVARCHAR. Technisch gesehen können Sie also nichts erstellen, das keinen Unicode-Namen hat 🙃.

Das Problem, das Sie hier haben, ist ausschließlich auf die Klassifizierung der verwendeten Zeichen zurückzuführen. Die Regeln für reguläre (dh nicht begrenzte) Bezeichner sind:

  • Der erste Buchstabe muss sein:
    • Ein Buchstabe gemäß Unicode-Standard 3.2.
    • Unterstrich (_), am Zeichen (@) oder Nummernzeichen (#)
  • Nachfolgende Buchstaben können sein:
    • Buchstaben wie im Unicode-Standard 3.2 definiert.
    • Dezimalzahlen aus einfachen lateinischen oder anderen nationalen Schriften.
    • Unterstrich (_), am Zeichen (@), Nummernzeichen (#) oder Dollarzeichen ($)
  • Eingebettete Leerzeichen oder Sonderzeichen sind nicht zulässig.
  • Ergänzungszeichen sind nicht erlaubt.

Ich habe die einzigen Regeln herausgearbeitet, die in diesem Zusammenhang von Bedeutung sind. Der Grund dafür, dass die Regeln für den "Anfangsbuchstaben" hier nicht relevant sind, ist, dass der Anfangsbuchstabe in allen lokalen Variablen und Parametern immer das "Vorzeichen" ist @.

Und um es klar auszudrücken: Was als "Buchstabe" und was als "Dezimalstelle" gilt, basiert auf den Eigenschaften , die jedem Zeichen in der Unicode-Zeichendatenbank zugewiesen sind. Unicode weist jedem Zeichen viele Eigenschaften zu, z. B. is_uppercase, is_lowercase, is_digit, is_decimal, is_combining usw. Dies ist keine Frage dessen, was wir Sterblichen als Buchstaben oder Dezimalstellen betrachten, sondern welchen Zeichen diese Eigenschaften zugewiesen wurden. Diese Eigenschaften werden häufig in regulären Ausdrücken verwendet, um eine Übereinstimmung mit "Interpunktion" usw. zu erzielen. Sie entsprechen beispielsweise \p{Lu}einem Großbuchstaben (in allen Sprachen / Skripten) und einem \p{IsDingbats}beliebigen "Dingbats" -Zeichen.

Also, in Ihrem Versuch zu tun:

DECLARE @¯\_(ツ)_ INT;

Nur die Zeichen _(Unterstrich oder "niedrige Linie") und (Katakana Letter Tu U + 30C4) passen in diese Regeln. Nun sind alle Zeichen in ¯\_(ツ)_/¯für durch Trennzeichen getrennte Bezeichner in Ordnung, aber es scheint leider, dass Variablen- / Parameternamen und GOTOBezeichnungen nicht getrennt werden können (obwohl Cursornamen möglich sind).

Bei Variablen- / Parameternamen müssen Sie daher nur Zeichen verwenden, die ab Unicode 3.2 entweder "Buchstaben" oder "Dezimalstellen" sind, da sie nicht voneinander abgegrenzt werden können (entsprechend der Dokumentation; ich muss testen) wenn Klassifizierungen für neuere Versionen von Unicode aktualisiert wurden, da Klassifizierungen anders behandelt werden als Sortiergewichtungen).

JEDOCH # 1 sind die Dinge nicht so einfach, wie sie sein sollten. Ich konnte nun meine Recherchen abschließen und habe festgestellt, dass die angegebene Definition nicht ganz korrekt ist. Die genaue (und überprüfbare) Definition, welche Zeichen für reguläre Bezeichner gültig sind, lautet:

  • Erstes Zeichen:

    • Kann alles sein, was in Unicode 3.2 als "ID_Start" klassifiziert ist (einschließlich "Letters", aber auch "letterlike numeric characters")
    • Kann _(niedrige Linie / Unterstrich) oder _(volle Breite niedrige Linie) sein
    • Kann sein @, aber nur für Variablen / Parameter
    • Kann sein #, aber wenn schemagebundenes Objekt, dann nur für Tabellen und gespeicherte Prozeduren (in diesem Fall geben sie an, dass das Objekt temporär ist)
  • Nachfolgende Zeichen:

    • Kann alles sein, was in Unicode 3.2 als "ID_Continue" klassifiziert ist (einschließlich "Dezimalzahlen", aber auch "Kombinationszeichen für Abstände und Leerzeichen" und "Interpunktionszeichen verbinden").
    • Sein kann @, #oder$
    • Kann eines der 26 Zeichen sein, die in Unicode 3.2 als Formatsteuerzeichen klassifiziert sind

(lustige Tatsache: die "ID" in "ID_Start" und "ID_Continue" steht für "Identifier". Stellen Sie sich das vor ;-)

Laut "Unicode Utilities: UnicodeSet":

  • Gültige Startzeichen

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

    -- 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'.
    */
    
  • Gültige Fortsetzungszeichen

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

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

JEDOCH # 2 , nicht einmal das Durchsuchen der Unicode-Datenbank kann so einfach sein. Diese beiden Suchvorgänge führen zu einer Liste gültiger Zeichen für diese Kategorisierungen. Diese Zeichen stammen aus Unicode 3.2, ABER die Definitionen der verschiedenen Kategorisierungen ändern sich in den Versionen des Unicode-Standards. Das heißt, die Definition von "ID_Start" in Unicode 10.0 (was diese Suche heute verwendet, 26.03.2018) entspricht nicht der Definition in Unicode 3.2. Die Online-Suche kann daher keine genaue Liste liefern. Sie können jedoch die Unicode 3.2-Datendateien abrufen und die Liste der Zeichen "ID_Start" und "ID_Continue" abrufen, um zu vergleichen, was SQL Server tatsächlich verwendet. Und ich habe dies getan und eine genaue Übereinstimmung mit den Regeln bestätigt, die ich oben in "JEDOCH # 1" angegeben habe.

In den folgenden beiden Blogeinträgen werden die Schritte zum Ermitteln der genauen Liste der Zeichen einschließlich der Links zu den Importskripten erläutert:

  1. Der Uni-Code: Die Suche nach der wahren Liste der gültigen Zeichen für reguläre T-SQL-Bezeichner, Teil 1
  2. Der Uni-Code: Die Suche nach der wahren Liste der gültigen Zeichen für reguläre T-SQL-Bezeichner, Teil 2

Für alle, die die Liste nur sehen möchten und sich nicht mit dem befassen, was für die Ermittlung und Überprüfung erforderlich ist, finden Sie diese Informationen hier:

Vollständig vollständige Liste der gültigen T-SQL-ID-Zeichen
(bitte geben Sie der Seite einen Moment Zeit zum Laden; es sind 3,5 MB und fast 47 KB Zeilen)


In Bezug auf „gültig“ ASCII - Zeichen, wie /und -arbeiten, nicht: das Problem hat nichts damit zu tun , ob die Zeichen auch im ASCII - Zeichensatz definiert. Um gültig zu sein, muss der Charakter entweder haben ID_Startoder das ID_ContinueEigentum oder eines der wenige benutzerdefinierten Zeichen separat zur Kenntnis genommen. Es gibt einige "gültige" ASCII-Zeichen (62 von insgesamt 128 - meist Interpunktions- und Steuerzeichen), die in "regulären" Bezeichnern nicht gültig sind.

Ergänzende Zeichen: Obwohl sie mit Sicherheit in begrenzten Bezeichnern verwendet werden können (und die Dokumentation anscheinend nicht anders aussagt), liegt dies höchstwahrscheinlich daran, dass sie nicht in regulären Bezeichnern verwendet werden können in integrierten Funktionen vor der Einführung von Supplementary Character-Aware Collations wurden in SQL Server 2012 eingeführt (sie werden als zwei einzelne "unbekannte" Zeichen behandelt). In nicht-binären Collations vor der Einführung von 100 konnten sie nicht einmal voneinander unterschieden werden. Sortierfolgen (eingeführt in SQL Server 2008).

In Bezug auf ASCII: 8-Bit-Codierungen werden hier nicht verwendet, da alle Bezeichner Unicode / NVARCHAR/ UTF-16 LE sind. Die Anweisung SELECT ASCII('ツ');gibt einen Wert zurück, der 63ein "?" (try:) SELECT CHAR(63);da dieses Zeichen, auch wenn es mit einem Großbuchstaben "N" versehen ist, sicher nicht in Codepage 1252 enthalten ist. Dieses Zeichen befindet sich jedoch in der koreanischen Codepage und liefert das richtige Ergebnis, auch ohne das "N" "Präfix in einer Datenbank mit einer koreanischen Standardsortierung:

SELECT UNICODE('ツ'); -- 12484

In Bezug auf den ersten Buchstaben, der das Ergebnis beeinflusst: Dies ist nicht möglich, da der erste Buchstabe für lokale Variablen und Parameter immer ist @. Der erste Buchstabe, den wir für diese Namen steuern können, ist das zweite Zeichen des Namens.

Bezüglich der Frage, warum lokale Variablennamen, Parameternamen und GOTOBezeichnungen nicht abgegrenzt werden können: Ich vermute, dass dies daran liegt, dass diese Elemente Teil der Sprache selbst sind und nicht als Daten in eine Systemtabelle gelangen.

Solomon Rutzky
quelle
Super, danke. Das hat mich zu diesem großartigen Blogbeitrag geführt: gist.github.com/BrentOzar/9b08b5ab2b617847dbe4aa0297b4cd5b
Brent Ozar
8
@BrentOzar Hattest du kürzlich einen CT-Scan?
Ross Presser
Wow, das ist eine beeindruckende Antwort! Und ich bin der Bemerkung von Ross Presser gefolgt.
SQL Nerd
22

Ich glaube nicht, dass Unicode das Problem verursacht. Bei lokalen Variablen- oder Parameternamen ist das Zeichen kein gültiges ASCII / Unicode 3.2-Zeichen (und es gibt keine Escape-Sequenz für Variablen / Parameter wie für andere Entitätstypen).

Dieser Batch funktioniert einwandfrei. Er verwendet ein Unicode-Zeichen, das die Regeln für nicht begrenzte Bezeichner einfach nicht verletzt:

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

Sobald Sie versuchen, einen Schrägstrich oder Bindestrich zu verwenden, bei denen es sich um gültige ASCII-Zeichen handelt, wird Folgendes bombardiert:

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

In der Dokumentation wird nicht erläutert, warum diese Bezeichner geringfügig anderen Regeln unterliegen als alle anderen Bezeichner oder warum sie nicht wie die anderen maskiert werden können.

Aaron Bertrand
quelle
Hallo Aaron. @Um hier einige Punkte zu verdeutlichen: 1) Das erste Zeichen ist kein Problem, da das erste Zeichen der Name var / param ist. Alle Zeichen, die nicht funktionieren, sollten an keiner Position funktionieren, auch wenn ihnen gültige Zeichen vorangestellt sind. 2) Das Dokument gibt nur an, dass Zusatzzeichen nicht in regulären Bezeichnern verwendet werden können (was für alle von mir ausgeführten Versuche der Fall zu sein scheint), sondern begrenzt begrenzte Bezeichner nicht, genau wie bei eingebetteten Leerzeichen. Ich glaube auch, dass dies anders ist, weil sie Teil der T-SQL-Sprache sind und nicht der Datenbank.
Solomon Rutzky
@SolomonRutzky Ich glaube, das Problem ist einfach und vollständig, dass ein Parametername nicht wie bei anderen Entitäten abgegrenzt werden kann. Wenn ich einen Parameternamen in eckige Klammern oder doppelte Anführungszeichen setzen könnte, könnte ich eines dieser Zeichen an einer beliebigen Position einfügen. In der Frage wird postuliert, dass Sie in einem Parameternamen keine Unicode-Zeichen verwenden können, was eindeutig nicht der Fall ist. Es gibt einige Unicode-Zeichen, die Sie verwenden können , und einige ASCII-Zeichen, die Sie nicht verwenden können .
Aaron Bertrand
Ja, ich bin damit einverstanden, dass GOTOdie einzige Einschränkung die Länge ist , wenn Variablen- / Parameternamen und Bezeichnungen getrennt werden dürfen. Ich kann nur davon ausgehen, dass das Parsen und / oder Behandeln dieser wenigen Elemente auf einer anderen Ebene erfolgt oder dass andere Einschränkungen dafür verantwortlich sind, dass begrenzte Werte nicht mehr ausgeführt werden können. Zumindest hoffe ich, dass es nicht willkürlich oder ein Versehen war.
Solomon Rutzky
(Ich habe die Aktualisierung Ihres Kommentars nicht gesehen, als ich vorhin geantwortet habe.) Ja, die Frage impliziert, dass das OP keine Unicode-Zeichen verwenden kann, aber die Formulierung der Frage ist technisch falsch, da alle Namen immer Unicode / NVARCHAR sind. Dies hat nichts mit ASCII zu tun, da dies eine 8-Bit-Codierung ist, die hier nicht verwendet wird. Alle Zeichen hier sind Unicode-Zeichen, auch wenn einige auch in verschiedenen 8-Bit-Codepages vorhanden sind. Wie ich in meiner Antwort erklärt habe, ist es eine Frage, welche Zeichen verwendet werden können, welche entweder mit is_alphabeticoder markiert wurden numeric_type=decimal.
Solomon Rutzky
Ich habe gespeicherte Procs gesehen, die voller Kacke waren, sie aber nie benannt haben!
Mitch Wheat