Як створити імена параметрів і змінних Unicode


53

Все це працює:

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

Але ви, напевно, можете побачити, куди я з цим іду: я не хочу @Shrug, я хочу @¯\_(ツ)_/¯.

Жодна з цих версій не працює в будь-якій версії 2008-2017 років:

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

Отже, чи існує спосіб використання назв параметрів процедури, що зберігаються unicode?

Відповіді:


44

Ну, ідентифікатори завжди є Unicode / NVARCHAR, тому технічно ви не можете створити нічого, що не має імені Unicode 🙃.

Проблема, яка виникає тут, повністю пов'язана з класифікацією символів, які використовуються. Правила для звичайних (тобто не обмежених) ідентифікаторів:

  • Перший лист повинен бути:
    • Лист, визначений стандартом Unicode 3.2.
    • підкреслення (_), знак (@) або знак цифри (#)
  • Наступні листи можуть бути:
    • Букви, як визначено у стандарті Unicode 3.2.
    • Десяткові числа з базової латини або інших національних сценаріїв.
    • підкреслення (_), знак (@), цифра (#) або знак долара ($)
  • Вбудовані пробіли або спеціальні символи заборонені.
  • Додаткові символи заборонені.

Я виділив жир єдиними правилами, які мають значення в цьому контексті. Причина того, що правила "Першої літери" тут не доречні, полягає в тому, що перша буква у всіх локальних змінних та параметрах завжди є "в знаку" @.

І щоб було зрозуміло: те, що вважається "літерою", а що вважається "десятковою цифрою", ґрунтується на властивостях, які кожному символу присвоєно в базі даних символів Unicode. Unicode призначає кожному символу багато властивостей, таких як: is_uppercase, is_lowercase, is_digit, is_decimal, is_combining тощо, тощо. Це не питання про те, що ми, смертні, вважатимемо літерами чи десятковими цифрами, але яким символам було призначено ці властивості. Ці властивості часто використовуються в регулярних виразах для відповідності "пунктуації" тощо. Наприклад, \p{Lu}відповідає будь-якій великій літері (для всіх мов / сценаріїв) та \p{IsDingbats}відповідає будь-якому символу "Дінгбати".

Отже, у вашій спробі зробити:

DECLARE @¯\_(ツ)_ INT;

у ці правила вписуються лише _символи (підкреслення або "низька лінія") та (літера Катакани Tu U + 30C4). Тепер усі символи ¯\_(ツ)_/¯вказують на розмежовані ідентифікатори, але, на жаль, схоже, що імена змінних / параметрів та GOTOміток не можна обмежувати (хоча імена курсорів можуть бути).

Отже, для імен змінних / параметрів, оскільки вони не можуть бути розмежовані, ви затримуєтесь лише тим, що використовуєте символи, які вважаються або "буквами", або "десятковими цифрами", як Unicode 3.2 (ну, згідно з документацією; мені потрібно перевірити якщо класифікації оновлено для новіших версій Unicode, оскільки класифікація обробляється інакше, ніж ваги сортування).

ЯКЩО №1 , все не так прямо, як має бути. Зараз я зміг завершити своє дослідження і виявив, що викладене визначення не зовсім правильне. Точне (і перевіряемое) визначення символів, дійсних для звичайних ідентифікаторів:

  • Перший персонаж:

    • У Unicode 3.2 може бути що-небудь класифіковане як "ID_Start" (що включає "Літери", але також "буквені цифрові символи")
    • Це може бути _(низька лінія / підкреслення) або _(низька лінія повної ширини)
    • Може бути @, але тільки для змінних / параметрів
    • Може бути #, але якщо об'єкт пов'язаний із схемою, то лише для таблиць і збережених процедур (у такому випадку вони вказують, що об'єкт тимчасовий)
  • Наступні символи:

    • У Unicode 3.2 може бути що-небудь класифіковане як "ID_Continue" (що включає "десяткові" числа, але також "інтервал та нерозміщення комбінуючих знаків" та "з'єднання розділових знаків")
    • Може бути @, #або$
    • Може бути будь-який з 26 символів, класифікованих в Unicode 3.2 як символи керування форматом

(цікавий факт: "ID" в "ID_Start" та "ID_Continue" означає "Ідентифікатор". Уявіть, що ;-)

Відповідно до "Утиліти Unicode: UnicodeSet":

  • Дійсні початкові символи

    [: Вік = 3.2:] & [: ID_Start = Так:]

    -- 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'.
    */
    
  • Дійсні символи продовження

    [: Вік = 3.2:] & [: ID_Continue = Так:]

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

ЗАРАЗ № 2 , навіть не пошук бази даних Unicode може бути таким простим. Ці два пошукові записи створюють список дійсних символів для цих категоризацій, і ці символи походять з Unicode 3.2, АЛЕ визначення різних категоризацій змінюється у версіях стандарту Unicode. Значення, визначення "ID_Start" в Unicode v 10.0 (для чого використовується цей пошук сьогодні, 2018-03-26) - це не те, що було в Unicode v 3.2. Отже, онлайн-пошук не може надати точного списку. Але ви можете захопити файли даних Unicode 3.2 та схопити список символів "ID_Start" та "ID_Continue", щоб порівняти з тим, що насправді використовує SQL Server. І я це зробив і підтвердив точну відповідність правилам, про які я говорив вище, у "ТАКОЖ №1".

У наступних двох публікаціях блогу детально описані кроки для пошуку точного списку символів, включаючи посилання на сценарії імпорту:

  1. Уні-код: Пошук справжнього списку дійсних символів для регулярних ідентифікаторів T-SQL, частина 1
  2. Уні-код: Пошук справжнього списку дійсних символів для регулярних ідентифікаторів T-SQL, частина 2

Нарешті, для тих, хто просто хоче переглянути список і не переймається тим, що потрібно для його виявлення та перевірки, можна знайти це тут:

Повноцінний список дійсних символів ідентифікаторів T-SQL
(будь-ласка, надайте сторінці момент для завантаження; це 3,5 Мб і майже 47 к рядків)


Що стосується "дійсних" символів ASCII, таких як /і -не працюють: питання не має нічого спільного з тим, чи визначені символи також у наборі символів ASCII. Щоб бути дійсним, персонаж повинен мати ID_Startабо ID_Continueвластивість, або властивість, або бути одним з небагатьох спеціальних символів, зазначених окремо. Існує досить багато "дійсних" символів ASCII (62 із 128 загальних знаків - переважно пунктуаційні та контрольні символи), які не дійсні в "Регулярних" ідентифікаторах.

Щодо додаткових символів: хоча вони, безумовно, можуть бути використані в обмежених ідентифікаторах (а документація, схоже, не вказує інакше), якщо це правда, що вони не можуть бути використані в звичайних ідентифікаторах, це, швидше за все, через їх повну підтримку вбудовані функції до допоміжних символів-зібрань, введених у SQL Server 2012 (вони розглядаються як два окремі "невідомі" символи), і їх не можна було навіть відрізнити один від одного у небінарних зіставленнях до початку 100- зіставлення рівня (впроваджено в SQL Server 2008).

Щодо ASCII: тут не використовуються 8-бітні кодування, оскільки всі ідентифікатори - це Unicode / NVARCHAR/ UTF-16 LE. Оператор SELECT ASCII('ツ');повертає значення, 63яке є "?" (спробуйте:), SELECT CHAR(63);оскільки цього символу, навіть якщо він є префіксом великого регістру "N", звичайно, немає у коді сторінки 1252. Однак цей символ знаходиться на Корейській сторінці Кореї, і він дає правильний результат навіть без "N "префікс у базі даних з корейським зіставленням за замовчуванням:

SELECT UNICODE('ツ'); -- 12484

Щодо першої літери, що впливає на результат: це неможливо, оскільки перша буква для локальних змінних та параметрів є завжди @. Перша літера, яку ми отримуємо для контролю над цими іменами, - це насправді 2-й символ імені.

Що стосується того, чому імена локальних змінних, назви параметрів та GOTOміток не можна обмежувати: я підозрюю, що це пов'язано з тим, що ці елементи є частиною самої мови, а не тим, що знайде свій шлях до системної таблиці як даних.


Так чудово, дякую. Це підштовхнуло мене до цього, що збирається зробити для чудової публікації в блозі: gist.github.com/BrentOzar/9b08b5ab2b617847dbe4aa0297b4cd5b
Brent Ozar

8
@BrentOzar У вас недавно було проведено КТ?
Росс Пресер

Ого, це досить вражаюча відповідь! І я другий зауваження Росса Пресера.
SQL Nerd

22

Я не думаю, що причиною проблеми є Unicode; у випадку імен локальних змінних чи параметрів, символ не є дійсним символом ASCII / Unicode 3.2 (і немає змінної послідовності для змінних / параметрів, як для інших типів сутності).

Ця партія працює чудово, вона використовує символ Unicode, який просто не порушує правила для нерозмежуваних ідентифікаторів:

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

Як тільки ви спробуєте використати косу рису або тире, обидва з них є дійсними символами ASCII, він бомбує:

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

Документація не стосується того, чому до цих ідентифікаторів поширюються дещо інші правила, ніж до всіх інших ідентифікаторів, або чому вони не можуть уникнути, як і інші.


Привіт Аарон. Просто для уточнення деяких моментів тут: 1) перший символ не є проблемою, оскільки перший символ насправді є @ім'ям var / param. Будь-який із символів, які не працюють, не повинен працювати в будь-якому положенні, навіть якщо йому передують дійсні символи. 2) doc заявляє лише про те, що додаткові символи не можна використовувати в звичайних ідентифікаторах (що, мабуть, має місце для всіх, що я пробував), але не встановлює обмежень на розмежовані ідентифікатори, як і вбудовані пробіли. Крім того, я вважаю, що вони різні, оскільки вони є частиною мови T-SQL, а не речей у БД.
Соломон Руцький

@SolomonRutzky Мені здається, що проблема полягає в тому, що назва параметра не може бути обмежена, як інші сутності. Якби я міг обернути квадратні дужки або подвійні лапки навколо імені параметра, я міг би помістити будь-який із цих символів у будь-яке положення. Питання постулює, що ви не можете використовувати символи Unicode в назві параметра, і це явно не так. Ви можете використовувати деякі символи Unicode , а також деякі символи ASCII, які ви не можете .
Аарон Бертран

Так, я погоджуюся, що якщо імена змінних / параметрів та GOTOміток дозволяється обмежувати, єдиним обмеженням буде довжина. Я можу лише припустити, що розбір та / або обробка цих кількох предметів відбувається на іншому рівні або мають деякі інші обмеження, які дозволяють зробити обмежені значення нездійсненними. Принаймні сподіваюся, що це не було довільним чи недоглядом.
Соломон Руцький

(я не бачив оновлення вашого коментаря, коли я відповів хвилину тому). Так, питання означає, що ОП не в змозі використовувати символи Unicode, але формулювання цього питання технічно неправильне, оскільки всі імена завжди є Unicode / NVARCHAR. Це не має нічого спільного з ASCII, оскільки це 8-бітове кодування, яке тут не використовується. Усі символи тут є символами Unicode, навіть якщо вони також існують у різних 8-бітових кодових сторінках. Як я пояснив у своїй відповіді, які символи можна використовувати - це питання, які з них позначені is_alphabeticабо numeric_type=decimal.
Соломон Руцький

Я бачив збережені програми, які були повними poo, але ніколи не називали його!
Мітч Пшеничний
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.