Cara membuat nama parameter dan variabel Unicode

53

Semua ini bekerja:

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

Tapi Anda mungkin bisa melihat ke mana saya akan pergi dengan ini: Saya tidak ingin @Shrug, saya ingin @¯\_(ツ)_/¯.

Tak satu pun dari ini bekerja pada versi apa pun dari 2008-2017:

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

Jadi, apakah ada cara untuk menggunakan nama parameter prosedur tersimpan unicode?

Brent Ozar
sumber

Jawaban:

44

Ya, pengidentifikasi selalu Unicode / NVARCHAR, jadi secara teknis Anda tidak dapat membuat apa pun yang tidak memiliki nama Unicode 🙃.

Masalah yang Anda alami di sini sepenuhnya disebabkan oleh klasifikasi karakter yang digunakan. Aturan untuk pengidentifikasi reguler (yaitu tidak dibatasi) adalah:

  • Huruf pertama harus:
    • Surat seperti yang didefinisikan oleh Unicode Standard 3.2.
    • garis bawah (_), pada tanda (@), atau tanda nomor (#)
  • Surat-surat selanjutnya dapat:
    • Surat-surat sebagaimana didefinisikan dalam Unicode Standard 3.2.
    • Angka desimal dari bahasa Latin Dasar atau skrip nasional lainnya.
    • garis bawah (_), pada tanda (@), tanda nomor (#), atau tanda dolar ($)
  • Spasi tertanam atau karakter khusus tidak diperbolehkan.
  • Karakter tambahan tidak diperbolehkan.

Saya berani satu-satunya aturan yang penting dalam konteks ini. Alasan bahwa aturan "Huruf pertama" tidak relevan di sini adalah bahwa huruf pertama di semua variabel dan parameter lokal selalu "di tanda" @.

Dan untuk menjadi jelas: apa yang dianggap sebagai "huruf" dan apa yang dianggap sebagai "angka desimal" didasarkan pada properti yang masing-masing karakter ditugaskan di Database Karakter Unicode. Unicode memberikan banyak properti untuk setiap karakter, seperti: is_uppercase, is_lowercase, is_digit, is_decimal, is_combining, dll, dll. Ini bukan masalah apa yang kita fana akan pertimbangkan sebagai huruf atau angka desimal, tetapi karakter mana yang telah menetapkan properti ini. Properti ini sering digunakan dalam Ekspresi Reguler untuk mencocokkan pada "tanda baca", dll. Misalnya, \p{Lu}cocok dengan huruf besar apa pun (di semua bahasa / skrip), dan \p{IsDingbats}cocok dengan karakter "Dingbats" apa pun.

Jadi, dalam upaya Anda untuk melakukan:

DECLARE @¯\_(ツ)_ INT;

hanya karakter _(garis bawah atau "garis rendah") dan (Katakana Letter Tu U + 30C4) yang sesuai dengan aturan tersebut. Sekarang, semua karakter di dalam ¯\_(ツ)_/¯adalah baik untuk pengidentifikasi dibatasi, tetapi sayangnya tampaknya variabel / nama parameter dan GOTOlabel tidak dapat dibatasi (meskipun nama kursor dapat).

Jadi, untuk nama variabel / parameter, karena mereka tidak dapat dibatasi, Anda terjebak dengan hanya menggunakan karakter yang memenuhi syarat sebagai "huruf" atau "angka desimal" pada Unicode 3.2 (baik, menurut dokumentasi; saya perlu menguji jika klasifikasi telah diperbarui untuk versi Unicode yang lebih baru karena klasifikasi ditangani secara berbeda dari bobot sortir).

NAMUN # 1 , segala sesuatu tidak semudah yang seharusnya. Saya sekarang dapat menyelesaikan penelitian saya dan menemukan bahwa definisi yang dinyatakan tidak sepenuhnya benar. Definisi tepat (dan dapat diverifikasi) karakter mana yang valid untuk pengidentifikasi reguler adalah:

  • Karakter pertama:

    • Dapat berupa apa saja yang diklasifikasikan dalam Unicode 3.2 sebagai "ID_Start" (yang meliputi "Surat" tetapi juga "karakter angka seperti huruf")
    • Bisa _(garis rendah / garis bawah) atau _(garis rendah bandwidth penuh)
    • Bisa jadi @, tetapi hanya untuk variabel / parameter
    • Dapat #, tetapi jika objek terikat skema, maka hanya untuk Tabel dan Prosedur yang Disimpan (dalam hal ini mereka menunjukkan bahwa objek bersifat sementara)
  • Karakter selanjutnya:

    • Dapat berupa apa saja yang diklasifikasikan dalam Unicode 3.2 sebagai "ID_Continue" (yang termasuk angka "desimal", tetapi juga "spasi dan nonspacing menggabungkan tanda", dan "menghubungkan tanda baca")
    • Bisa @, #, atau$
    • Dapat berupa 26 karakter yang diklasifikasikan dalam Unicode 3.2 sebagai karakter kontrol format

(Fakta menyenangkan: "ID" di "ID_Start" dan "ID_Continue" adalah singkatan dari "Identifier". Bayangkan ;-)

Menurut "Unicode Utilities: UnicodeSet":

  • Karakter awal yang valid

    [: Usia = 3,2:] & [: ID_Start = Ya:]

    -- 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'.
    */
    
  • Karakter kelanjutan yang valid

    [: Usia = 3,2:] & [: ID_Continue = Ya:]

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

NAMUN # 2 , bahkan tidak mencari database Unicode bisa semudah itu. Kedua pencarian tersebut menghasilkan daftar karakter yang valid untuk kategorisasi tersebut, dan karakter tersebut berasal dari Unicode 3.2, TETAPI definisi dari berbagai perubahan kategorisasi di seluruh versi Standar Unicode. Artinya, definisi "ID_Start" di Unicode v 10.0 (apa yang digunakan pencarian hari ini, 2018-03-26) tidak seperti di Unicode v 3.2. Jadi, pencarian online tidak dapat memberikan daftar yang tepat. Tetapi Anda dapat mengambil file data Unicode 3.2 dan mengambil daftar karakter "ID_Start" dan "ID_Continue" dari sana untuk membandingkan dengan apa yang sebenarnya digunakan oleh SQL Server. Dan saya telah melakukan ini dan mengonfirmasi kecocokan persis dengan aturan yang saya nyatakan di atas dalam "NAMUN # 1".

Dua posting blog berikut merinci langkah-langkah yang diambil untuk menemukan daftar karakter yang tepat, termasuk tautan ke skrip impor:

  1. The Uni-Code: Pencarian untuk Daftar True Karakter Valid untuk Pengidentifikasi Reguler T-SQL, Bagian 1
  2. The Uni-Code: Pencarian untuk Daftar True Karakter Valid untuk Pengidentifikasi Reguler T-SQL, Bagian 2

Akhirnya, bagi siapa saja yang hanya ingin melihat daftar dan tidak peduli dengan apa yang diperlukan untuk menemukan dan memverifikasinya, Anda dapat menemukannya di sini:

Daftar Lengkap Lengkap dari Karakter T-SQL Identifier yang Valid
(tolong beri halaman waktu untuk memuat; itu 3,5 MB dan hampir 47k baris)


Mengenai karakter ASCII "valid", seperti /dan -, tidak berfungsi: masalah tidak ada hubungannya dengan apakah karakter tersebut juga ditentukan dalam karakter ASCII atau tidak. Agar valid, karakter harus memiliki baik ID_Startatau ID_Continueproperti, atau menjadi salah satu dari beberapa karakter kustom mencatat secara terpisah. Ada beberapa karakter ASCII "valid" (62 dari 128 total - sebagian besar tanda baca dan karakter kontrol) yang tidak valid dalam Identifikasi "Biasa".

Mengenai Karakter Tambahan: walaupun mereka tentu saja dapat digunakan dalam pengidentifikasi terbatas (dan dokumentasi tampaknya tidak menyatakan sebaliknya), jika memang benar mereka tidak dapat digunakan dalam pengidentifikasi reguler, kemungkinan besar karena mereka tidak didukung sepenuhnya dalam fungsi bawaan sebelum Pelengkap Sadar Karakter-Sadar diperkenalkan dalam SQL Server 2012 (mereka diperlakukan sebagai dua karakter "tidak dikenal" individu), mereka bahkan tidak dapat dibedakan satu sama lain dalam Koleksi non-biner sebelum 100- level Collations (diperkenalkan pada SQL Server 2008).

Mengenai ASCII: penyandian 8-bit tidak digunakan di sini karena semua pengidentifikasi adalah Unicode / NVARCHAR/ UTF-16 LE. Pernyataan SELECT ASCII('ツ');mengembalikan nilai 63yang merupakan "?" (coba:) SELECT CHAR(63);karena karakter itu, bahkan jika diawali dengan huruf besar "N", tentu saja tidak ada dalam Kode Page 1252. Namun, karakter itu ada di Halaman Kode Korea dan menghasilkan hasil yang benar, bahkan tanpa "N "awalan, dalam Database dengan Collation default Korea:

SELECT UNICODE('ツ'); -- 12484

Mengenai huruf pertama yang mempengaruhi hasil: ini tidak mungkin karena huruf pertama untuk variabel dan parameter lokal selalu @. Huruf pertama yang bisa kita kontrol untuk nama-nama ini sebenarnya adalah karakter ke-2 dari nama tersebut.

Mengenai mengapa nama variabel lokal, nama parameter, dan GOTOlabel tidak dapat dibatasi: Saya menduga ini karena item-item ini menjadi bagian dari bahasa itu sendiri dan bukan sesuatu yang akan menemukan jalannya ke tabel sistem sebagai data.

Solomon Rutzky
sumber
Luar biasa, terima kasih. Itu membawa saya ke ini, yang akan membuat posting blog yang bagus: gist.github.com/BrentOzar/9b08b5ab2b617847dbe4aa0297b4cd5b
Brent Ozar
8
@BrentOzar, apakah Anda sudah menjalani CT scan baru-baru ini?
Ross Presser
Wow, itu jawaban yang cukup mengesankan! Dan saya mendukung komentar Ross Presser.
SQL Nerd
22

Saya tidak berpikir itu adalah Unicode yang menyebabkan masalah; dalam kasus variabel lokal atau nama parameter, karakternya bukan ASCII / Unicode 3.2 karakter yang valid (dan tidak ada urutan melarikan diri untuk variabel / parameter seperti ada untuk jenis entitas lainnya).

Batch ini berfungsi dengan baik, ia menggunakan karakter Unicode yang sama sekali tidak melanggar aturan untuk pengidentifikasi yang tidak dibatasi:

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

Segera setelah Anda mencoba menggunakan garis miring atau garis putus-putus, keduanya karakter ASCII yang valid, itu mengebom:

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

Dokumentasi tidak membahas mengapa ini pengidentifikasi tunduk pada aturan yang sedikit berbeda dari semua pengenal lainnya, atau mengapa mereka tidak dapat melarikan diri seperti yang lain.

Aaron Bertrand
sumber
Hai Harun. Hanya untuk memperjelas beberapa poin di sini: 1) karakter pertama tidak menjadi masalah karena karakter pertama sebenarnya adalah @dari nama var / param. Karakter apa pun yang tidak berfungsi seharusnya tidak berfungsi di posisi apa pun, meskipun didahului oleh karakter yang valid. 2) dokumen hanya menyatakan bahwa karakter tambahan tidak dapat digunakan dalam pengidentifikasi reguler (yang tampaknya merupakan kasus untuk semua yang telah saya coba), tetapi tidak menempatkan batasan pada pengidentifikasi terbatas, seperti halnya dengan ruang yang disematkan. Juga, saya percaya ini berbeda karena mereka adalah bagian dari bahasa T-SQL, bukan hal-hal di DB.
Solomon Rutzky
@ SolomonRutzky Saya merasa seperti masalahnya sederhana dan seluruhnya bahwa nama parameter tidak dapat dibatasi seperti entitas lain. Jika saya bisa membungkus tanda kurung siku atau tanda kutip ganda di sekitar nama parameter, saya bisa memasukkan salah satu karakter ini ke dalamnya, di posisi apa pun. Pertanyaan tersebut mendalilkan bahwa Anda tidak dapat menggunakan karakter Unicode dalam nama parameter, dan itu jelas bukan masalahnya. Ada beberapa karakter Unicode yang dapat Anda gunakan, dan beberapa karakter ASCII Anda tidak bisa .
Aaron Bertrand
Ya, saya setuju bahwa jika nama dan GOTOlabel variabel / parameter dibolehkan untuk dibatasi, maka satu-satunya batasan adalah panjangnya. Saya hanya dapat berasumsi bahwa penguraian dan / atau penanganan beberapa item tersebut terjadi pada level yang berbeda atau memiliki beberapa kendala lain yang membuat memungkinkan untuk nilai yang dibatasi tidak dapat bekerja. Setidaknya saya berharap itu tidak sewenang-wenang atau kelalaian.
Solomon Rutzky
(belum melihat pembaruan untuk komentar Anda ketika saya menjawab beberapa saat yang lalu). Ya, pertanyaannya menyiratkan bahwa OP tidak dapat menggunakan karakter Unicode, tetapi frasa pertanyaan secara teknis salah karena semua nama selalu Unicode / NVARCHAR. Ini tidak ada hubungannya dengan ASCII karena itu adalah pengkodean 8-bit yang tidak digunakan di sini. Semua karakter di sini adalah karakter Unicode, bahkan jika beberapa juga ada di berbagai halaman kode 8-bit. Seperti yang saya jelaskan dalam jawaban saya, karakter mana yang dapat digunakan adalah soal yang mana ditandai dengan salah satu is_alphabeticatau numeric_type=decimal.
Solomon Rutzky
Saya telah melihat procs tersimpan yang penuh dengan kotoran tetapi tidak pernah menamainya!
Mitch Wheat