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存储过程参数名? />
#1 楼
好吧,标识符始终是Unicode /NVARCHAR
,因此从技术上讲,您不能创建没有Unicode名称🙃的任何内容。您遇到的问题完全是由于字符的分类(s)正在使用。常规(即非定界)标识符的规则为:
首字母必须为:
Unicode标准定义的字母3.2。
下划线(_),符号(@)或数字符号(#)
后续字母可以是:
Unicode标准3.2中定义的字母。
基本拉丁字母或其他国家文字的十进制数字。
下划线(_),符号(@),数字符号(#)或美元符号($)
不允许包含空格或特殊字符。
不允许使用辅助字符。
我加粗了唯一重要的规则在这种情况下。 “首字母”规则在这里不相关的原因是,所有局部变量和参数中的首字母始终是“在符号”
@
。要明确:什么被认为是“字母”以及所谓的“十进制数字”是基于Unicode字符数据库中分配给每个字符的属性。 Unicode为每个字符分配许多属性,例如:is_uppercase,is_lowercase,is_digit,is_decimal,is_combining等。这与我们凡人认为字母或十进制数字无关,但已为这些字符分配了这些属性。这些属性通常在正则表达式中用于匹配“标点”等。例如,
\p{Lu}
匹配任何大写字母(跨所有语言/脚本),而\p{IsDingbats}
匹配任何“ Dingbats”字符。,因此,您尝试执行以下操作:
DECLARE @¯\_(ツ)_/¯ INT;
只有
_
(下划线或“下划线”)和ツ
(片假名字母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 =是:]
-- 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'.
*/
有效的连续字符
[:Age = 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标准的各个版本中都会发生变化。意思是,Unicode v 10.0中的“ ID_Start”的定义(该搜索今天使用的是2018-03-26)与Unicode v 3.2中的定义不同。因此,在线搜索无法提供确切的列表。但是,您可以获取Unicode 3.2数据文件,并从此处获取“ ID_Start”和“ ID_Continue”字符列表,以与SQL Server实际使用的内容进行比较。我已经做到了这一点,并确认与我在“ HOWEVER#1”中上面提到的规则完全匹配。
以下两个博客文章详细介绍了用于查找字符的确切列表的步骤,包括链接导入脚本:
Uni-Code:搜索T-SQL常规标识符的有效字符的真实列表,第1部分
Uni-Code:The搜索T-SQL常规标识符的有效字符的真实列表,第2部分
最后,对于只想查看该列表而不关心发现和验证它的人,您可以在此处找到:
有效的T-SQL标识符字符的完整列表
(请给页面一点时间来加载;它的大小为3.5 MB,几乎是47k行)
关于诸如
/
和-
之类的“有效” ASCII字符不起作用:问题与字符是否也在ASCII字符集中定义无关。为了有效,该字符必须具有ID_Start
或ID_Continue
属性,或者是单独记录的少数几个自定义字符之一。有很多“有效” ASCII字符(总共128个中的62个,主要是标点和控制字符)在“常规”标识符中无效。关于补充字符:尽管它们肯定可以如果确实不能将它们用于常规标识符,则很有可能是因为在分隔符中使用了分隔符(并且文档中似乎没有其他说明),这很可能是由于补充字符之前的内置函数未完全支持它们-在SQL Server 2012中引入了感知排序规则(它们被视为两个单独的“未知”字符),甚至在100级排序规则(在SQL Server 2008中引入)之前的非二进制排序规则中也无法将它们彼此区分开。 。
关于ASCII:由于所有标识符均为Unicode /
NVARCHAR
/ UTF-16 LE,因此此处未使用8位编码。语句SELECT ASCII('ツ');
返回值为“?”的63
。 (请尝试:SELECT CHAR(63);
),因为该字符即使以大写字母“ N”为前缀也肯定不在代码页1252中。但是,该字符在朝鲜语代码页中并且即使没有“ N”前缀,在具有韩文默认排序规则的数据库中:SELECT UNICODE('ツ'); -- 12484
关于影响结果的第一个字母:这是不可能的,因为局部变量和参数始终为
@
。我们控制这些名称的第一个字母实际上是名称的第二个字符。关于为什么不能区分局部变量名称,参数名称和
GOTO
标签的原因:我怀疑这是由于这些项目是语言本身的一部分,而不是将其作为数据进入系统表的方式。#2 楼
我不认为是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语言的一部分,而不是数据库中的东西。
–所罗门·鲁兹基
18年3月19日在15:45
@SolomonRutzky我觉得问题很简单,那就是参数名称不能像其他实体一样被定界。如果我可以将方括号或双引号括在参数名称周围,则可以将这些字符中的任何一个放在任何位置。该问题假定您不能在参数名称中使用Unicode字符,显然不是这种情况。您可以使用某些Unicode字符,而不能使用某些ASCII字符。
–亚伦·伯特兰(Aaron Bertrand)
18-3-19在15:50
是的,我同意如果允许对变量/参数名称和GOTO标签进行分隔,那么唯一的限制就是长度。我只能假设,解析和/或处理这几项的操作发生在不同的级别,或者存在其他一些限制,使得使用分隔值不可行。至少我希望这不是武断或疏忽大意。
–所罗门·鲁兹基
18年3月19日在16:01
(我刚才回答时未看到您评论的更新)。是的,这个问题确实暗示O.P.无法使用Unicode字符,但是该问题的措词在技术上是不正确的,因为所有名称始终都是Unicode / NVARCHAR。这与ASCII没有关系,因为这是8位编码,在此未使用。即使所有的8位代码页中也存在某些字符,此处的所有字符都是Unicode字符。正如我在回答中所解释的那样,可以使用哪些字符与使用is_alphabetic或numeric_type = decimal标记的字符有关。
–所罗门·鲁兹基
18年3月19日在16:15
我看过存储过程中充满了便便但从未命名!
–米奇小麦
18 Mar 27 '18 at 4:58
评论
太棒了,谢谢。这使我想到了这一点,这将成为一个很棒的博客文章:gist.github.com/BrentOzar/9b08b5ab2b617847dbe4aa0297b4cd5b
–布伦特·奥扎(Brent Ozar)
18年3月19日在9:32
@BrentOzar您最近进行过CT扫描吗?
–罗斯压机
18年3月20日在21:01
哇,这真是一个了不起的答案!我赞同罗斯·普纳斯的话。
– SQL Nerd
18年4月12日在4:06