如何创建 Unicode 参数和变量名称

Bre*_*zar 53 sql-server stored-procedures t-sql parameter unicode

所有这些都有效:

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
Run Code Online (Sandbox Code Playgroud)

但是您可能会看到我的意思:我不想要 @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
Run Code Online (Sandbox Code Playgroud)

那么,有没有办法使用 unicode 存储过程参数名称?

Sol*_*zky 44

好吧,标识符总是 Unicode / NVARCHAR,所以从技术上讲,你不能创建任何没有 Unicode 名称的东西?。

您在这里遇到的问题完全是由于使用的字符的分类。常规(即非分隔)标识符的规则是:

  • 第一个字母必须是:
    • Unicode 标准 3.2 定义的字母。
    • 下划线 (_)、at 符号 (@) 或数字符号 (#)
  • 后续字母可以是:
    • Unicode 标准 3.2 中定义的字母。
    • 来自基本拉丁文或其他国家文字的十进制数字。
    • 下划线 (_)、at 符号 (@)、数字符号 (#) 或美元符号 ($)
  • 不允许嵌入空格或特殊字符。
  • 不允许使用补充字符。

在这种情况下,我将唯一重要的规则加粗。“首字母”规则在这里不相关的原因是所有局部变量和参数中的首字母始终是“at 符号” @

需要明确的是:什么被认为是“字母”,什么被认为是“十进制数字”是基于每个字符在 Unicode 字符数据库中分配的属性。Unicode 为每个字符分配了许多属性,例如:is_uppercase、is_lowercase、is_digit、is_decimal、is_combining 等。这不是我们凡人会考虑的字母或十进制数字的问题,而是哪些字符被分配了这些属性。这些属性通常在正则表达式中用于匹配“标点符号”等。例如,\p{Lu}匹配任何大写字母(跨所有语言/脚本),并\p{IsDingbats}匹配任何“Dingbats”字符。

因此,在您尝试执行以下操作时:

DECLARE @¯\_(?)_/¯ INT;
Run Code Online (Sandbox Code Playgroud)

只有_(下划线或“低线”)和?(片假名字母 Tu U+30C4)字符符合这些规则。现在,中的所有字符¯\_(?)_/¯都可以用作分隔标识符,但不幸的是,变量/参数名称和GOTO标签似乎无法分隔(尽管游标名称可以)。

因此,对于变量/参数名称,由于它们无法分隔,因此您只能使用符合 Unicode 3.2 标准的“字母”或“十进制数字”的字符(好吧,根据文档;我需要测试如果分类已针对较新版本的 Unicode 更新,因为分类的处理方式与排序权重不同)。

然而#1,事情并不像它们应该的那样简单。我现在已经能够完成我的研究,并发现所述定义并不完全正确。哪些字符对常规标识符有效的精确(和可验证)定义是:

  • 第一个字符:

    • 可以是Unicode 3.2 中归类为“ID_Start”的任何内容(包括“字母”但也包括“类似字母的数字字符”)
    • 可以是_(低线/下划线)或?(全宽低线)
    • 可以@,但适用于变量/参数
    • 可以#,但如果是模式绑定对象,则仅适用于表和存储过程(在这种情况下,它们表示对象是临时的)
  • 后续字符:

    • 可以是Unicode 3.2 中归类为“ID_Continue”的任何内容(包括“十进制”数字,但也包括“空格和非空格组合标记”和“连接标点符号”)
    • 可以是@, #, 或$
    • 可以是Unicode 3.2 中归类为格式控制字符的 26 个字符中的任何一个

(有趣的事实:“ID_Start”和“ID_Continue”中的“ID”代表“标识符”。想象一下;-)

根据“Unicode 实用程序:UnicodeSet”:

  • 有效的起始字符

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

    -- 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'.
    */
    
    Run Code Online (Sandbox Code Playgroud)
  • 有效的连续字符

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

    -- Test various decimal numbers, but none are Supplementary Characters
    DECLARE @??????? 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
    
    Run Code Online (Sandbox Code Playgroud)

然而 #2,即使搜索 Unicode 数据库也不是那么容易。这两个搜索确实为这些分类生成了一个有效字符列表,这些字符来自 Unicode 3.2,但是各种分类的定义会随着 Unicode 标准的版本而变化。这意味着,以Unicode v 10.0“ID_Start”的定义(这是什么搜索使用的今天,2018年3月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 MB,几乎 47k 行)


关于“有效” ASCII 字符,例如/-,不起作用:该问题与字符是否也在 ASCII 字符集中定义无关。为了有效,字符必须具有ID_StartID_Continue属性,或者是单独注明的少数自定义字符之一。有相当多的“有效”ASCII 字符(总共 128 个中的 62 个——主要是标点符号和控制字符)在“常规”标识符中无效。

关于补充字符:虽然它们当然可以用于分隔标识符(并且文档似乎没有另外说明),但如果它们确实不能用于常规标识符,那很可能是因为它们没有得到完全支持在 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
Run Code Online (Sandbox Code Playgroud)

关于影响结果的第一个字母:这是不可能的,因为局部变量和参数的第一个字母总是@。我们控制这些名称的第一个字母实际上是名称的第二个字符。

关于为什么GOTO不能分隔局部变量名、参数名和标签:我怀疑这是因为这些项目是语言本身的一部分,而不是作为数据进入系统表的东西。

  • @BrentOzar 你最近做过 CT 扫描吗? (8认同)

Aar*_*and 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;
Run Code Online (Sandbox Code Playgroud)

一旦你尝试使用斜杠或破折号,这两个都是有效的 ASCII 字符,它就会爆炸:

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

该文档没有说明为什么这些标识符与所有其他标识符的规则略有不同,或者为什么它们不能像其他标识符一样被转义。