Как создать 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.
    • Десятичные числа из базовой латиницы или других национальных сценариев.
    • подчеркивание (_), знак (@), знак числа (#) или знак доллара ($)
  • Встроенные пробелы или специальные символы не допускаются.
  • Дополнительные символы не допускаются.

Я выделил единственные правила, которые имеют значение в этом контексте. Причина, по которой правила «первой буквы» здесь не актуальны, заключается в том, что первая буква во всех локальных переменных и параметрах всегда является знаком «at» @.

И чтобы быть ясным: то, что считается «буквой», а то, что считается «десятичной цифрой», основано на свойствах, которые присваиваются каждому символу в базе данных символов Unicode. Юникод присваивает каждому символу множество свойств, таких как: is_uppercase, is_lowercase, is_digit, is_decimal, is_combining и т. Д. И т. Д. Это не вопрос того, что мы, смертные, будем считать буквами или десятичными цифрами, но каким символам были назначены эти свойства. Эти свойства часто используются в регулярных выражениях для сопоставления с «пунктуацией» и т. Д. Например, \p{Lu}сопоставление с любой заглавной буквой (для всех языков / сценариев) и \p{IsDingbats}сопоставление с любым символом «дингбаты».

Итак, в вашей попытке сделать:

DECLARE @¯\_(ツ)_ INT;

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

Таким образом, для имен переменных / параметров, поскольку они не могут быть разделены, вы застряли с использованием только символов, которые квалифицируются как «буквы» или «десятичные цифры» в Unicode 3.2 (ну, согласно документации; мне нужно проверить если классификации были обновлены для более новых версий Unicode, поскольку классификации обрабатываются не так, как веса сортировки).

ОДНАКО , вещи не так просты, как должны быть. Теперь я смог завершить свое исследование и обнаружил, что указанное определение не совсем правильно. Точное (и проверяемое) определение того, какие символы действительны для обычных идентификаторов:

  • Первый персонаж:

    • Может быть чем-то, что в Unicode 3.2 классифицировано как «ID_Start» (которое включает в себя «буквы», но также «буквенно-цифровые символы»)
    • Может быть _( _нижняя строка / подчеркивание) или (нижняя строка полной ширины)
    • Может быть @, но только для переменных / параметров
    • Может быть #, но если объект привязан к схеме, то только для таблиц и хранимых процедур (в этом случае они указывают, что объект является временным)
  • Последующие персонажи:

    • Может быть чем-то, что в Unicode 3.2 классифицируется как «ID_Continue» (которое включает в себя «десятичные» числа, а также «пробелы и непересекающиеся знаки объединения» и «соединительные знаки препинания»)
    • Может быть @, #или$
    • Может быть любым из 26 символов, классифицированных в Unicode 3.2 как символы управления форматом

(забавный факт: «ID» в «ID_Start» и «ID_Continue» означает «Идентификатор». Представьте себе, что ;-)

Согласно «Утилиты Юникод: 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. Uni-Code: Поиск истинного списка допустимых символов для регулярных идентификаторов T-SQL, часть 1
  2. Uni-Code: Поиск истинного списка допустимых символов для регулярных идентификаторов T-SQL, часть 2

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

Полностью полный список допустимых символов идентификатора T-SQL
(пожалуйста, дайте странице время для загрузки; это 3,5 МБ и почти 47 000 строк)


Относительно «действительных» символов 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которое является "?" (try:), SELECT CHAR(63);поскольку этот символ, даже если с префиксом в верхнем регистре "N", определенно отсутствует в кодовой странице 1252. Однако этот символ находится в корейской кодовой странице и дает правильный результат, даже без "N" "префикс в базе данных с сопоставлением по-корейски по умолчанию:

SELECT UNICODE('ツ'); -- 12484

Что касается первой буквы, влияющей на результат: это невозможно, поскольку первая буква для локальных переменных и параметров всегда @. Первая буква, которую мы получаем для контроля над этими именами, на самом деле является вторым символом имени.

Относительно того, почему имена локальных переменных, имена параметров и GOTOметки не могут быть разделены: я подозреваю, что это связано с тем, что эти элементы являются частью самого языка, а не тем, что попадает в системную таблицу в качестве данных.

Соломон Руцкий
источник
Так здорово, спасибо. Это привело меня к этому, что послужит
Брент Озар
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) в документе указано только то, что дополнительные символы не могут использоваться в обычных идентификаторах (что, похоже, имеет место для всего, что я пробовал), но не накладывает ограничений на идентификаторы с разделителями, как во встроенных пробелах. Кроме того, я считаю, что они разные, потому что они являются частью языка T-SQL, а не вещами в БД.
Соломон Руцки
@SolomonRutzky Я чувствую, что проблема в том, что имя параметра не может быть разделено, как другие объекты. Если бы я мог обернуть квадратные скобки или двойные кавычки вокруг имени параметра, я мог бы поместить любой из этих символов в него в любой позиции. Вопрос постулирует, что вы не можете использовать символы Юникода в имени параметра, и это явно не так. Есть некоторые символы Юникода, которые вы можете использовать, и некоторые символы ASCII, которые вы не можете .
Аарон Бертран
Да, я согласен с тем, что если имена переменных и параметров и GOTOметки допускают разделение, то единственным ограничением будет длина. Я могу только предположить, что анализ и / или обработка этих нескольких элементов происходит на другом уровне или имеет некоторые другие ограничения, которые сделали невозможным использование значений с разделителями. По крайней мере, я надеюсь, что это не было произволом или недосмотром.
Соломон Руцки
(не видел обновления вашего комментария, когда я отвечал минуту назад). Да, вопрос подразумевает, что OP не может использовать символы Unicode, но формулировка вопроса технически неверна, поскольку все имена всегда Unicode / NVARCHAR. Это не имеет ничего общего с ASCII, поскольку это 8-битная кодировка, которая здесь не используется. Все символы здесь являются символами Юникода, даже если некоторые из них существуют в различных 8-битных кодовых страницах. Как я объяснил в своем ответе, какие символы можно использовать, зависит от того, какие из них были помечены is_alphabeticили numeric_type=decimal.
Соломон Руцки
Я видел хранимые проки, которые были полны пу, но никогда не называли его!
Митч Пшеничный