Як створити імена параметрів і змінних 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 @ᔠᑥᑒᏯשፙᇏᆇᄳᄈლဪඤ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'.
    */
    
  • Дійсні символи продовження

    [: Вік = 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.