为什么对象名称不能以数字开头?

Jam*_*mes 7 sql-server identifier

例如,如果我使用 name 创建视图'4aii',为什么 SQL Server 关心它以 开头4?我可以打电话给桌子FouraiiIVaii

此外,[]在幕后做什么以允许将任何字符串用作名称?

一根绳子就是一根绳子,amirite?

mus*_*cio 20

首先,您需要区分数字(数字文字)、字符串(字符串文字)和标识符。'4aii'是一个字符串文字,可以是某个“事物”的值,但它不标识(命名)事物。4aii[4aii]将是标识符(如果允许的话)。

查询解析器需要了解它正在查看的标记的含义。通过允许名称以数字开头,您可以通过扩展允许它们完全由数字组成。然后,给定select 12345 from mytable,您(和解析器)如何知道12345是整数文字还是列名?

但是,如果您允许标识符仅以字母(或下划线字符)开头,您可以明确指出您是在查看标识符 ( abc123) 还是字符串文字 ( 'abc123') —— 后者用引号括起来。

SQL Server 中的方括号、MySQL 中的反引号 (`) 和 ANSI SQL 兼容引擎中的双引号表示标识符,当您的标识符无法与其他标记轻松区分时,您可以使用它们:以数字开头、有空格或它们中的其他特殊字符等等。因此,[4aii]或者"4aii"清楚地告诉解析器它正在处理一个标识符。

一个小 dbfiddle 演示。


Sol*_*zky 17

一根绳子就是一根绳子,amirite?

是和否:字符串字符串,对象/项目名称不是字符串。因此,虽然该陈述是正确的,但它也与您所看到的行为无关。

忽略特定规则的概念推理,“为什么一个有效而另一个无效”的技术答案是 SQL Server 遵循(最小化定制)Unicode 标准的标识符指南。Unicode 文档可以在这里找到:

Unicode® 标准附件 #31:UNICODE 标识符和模式语法

未包含在其中的标识符[...]"..."为“常规”标识符,而包含在其中的标识符为“分隔”标识符。常规标识符是在所有上下文中都有效的名称(即这些是在这种语言、软件等中命名事物的规则)。分隔标识符是其他一切:无效且不应该工作的名称,但是,如果您将它们包装在这些分隔符中的任何一个中,它们将获得豁免。大多数标识符都可以分隔;它只是GOTO标签和变量(包括表变量)/无法分隔的参数。区别似乎在于,纯粹为在 T-SQL 语言中使用而存在的标识符(即,不会作为元数据存储在数据文件或日志文件中的名称)不能被分隔(正如您在任何语言)。

现在,SQL Server 文档并不完全完整/正确,但是关于来自 Unicode 3.2 的有效“标识符”字符(包括开始和继续)的分类是正确的。如果您想要常规标识符和分隔标识符的实际规则列表,我将它们记录在此处:

完整的 T-SQL 标识符规则列表

要查看证明 Unicode 3.2 分类与 SQL Server 接受的常规标识符之间关系的研究,请访问:

  1. 统一代码:搜索 T-SQL 正则标识符的真实有效字符列表,第 1 部分
  2. 统一代码:搜索 T-SQL 正则标识符的真实有效字符列表,第 2 部分

解决对此答案的评论中指出的问题:

  1. 是的,即使允许非分隔标识符与启动_#以及@ 占的统一规范。第1.2节地址定制到基座规则,甚至提供了四个示例的自定义:_#@,和$。这 4 个“潜在”自定义项与 SQL Server 使用的 4 个完全相同。因此,SQL服务器允许@Variable#TempTable不会从这个文件的Unicode点离开作为规则的来源。
  2. 如上所述,SQL Server 文档确实声明所使用的分类来自 Unicode 字符数据库的 3.2 版,它们目前是 10 版。您不能使用Ident_*的当前定义,如在 Unicode 网站上找到的那样,如指示有效/无效字符。字符添加到Ident_StartIdent_ContinueUnicode标准的每个新版本。查看与这些属性匹配的正确字符集的唯一方法是下载 Unicode 版本 3.2。
  3. 以上两点都在上面直接提到的两篇博客文章中进行了处理(名为“统一代码:搜索 T-SQL 正则标识符的有效字符的真实列表”)。在将此答案视为不正确之前,请阅读这两篇文章。我在这两篇文章中提到了这里实际发生的事情背后有很多细微差别,逐步展示了如何匹配有效字符列表。

另外,关于标题中所述的问题,这取决于您对“数字”的定义松散程度。意思是,如果您按照上面直接提到的两篇文章中所示的研究步骤进行操作,这样您就创建了一个表来保存 Unicode 字符数据库 v3.2 和一些其他属性,您可以获得 52 个非- 字母(主要是“数字”)是通过以下查询开始标识符的有效字符:

SELECT ucd.*
FROM   [v3-2].UnicodeCharacterDatabase ucd
WHERE  ucd.[IDStart] = 1
AND    ucd.[GeneralCategory] NOT LIKE 'L%';
Run Code Online (Sandbox Code Playgroud)

选择其中一些字符进行测试,我们可以看到它们确实有效:

USE [tempdb];
CREATE TABLE dbo.?aii ([Col1] INT); -- ROMAN NUMERAL FOUR (U+2163)

CREATE TABLE dbo.?aii ([Col1] INT); -- ROMAN NUMERAL TEN THOUSAND (U+2182)

CREATE TABLE dbo.?aii ([Col1] INT); -- HANGZHOU NUMERAL FOUR (U+3024)
Run Code Online (Sandbox Code Playgroud)

并且,为了表明它们不仅仅是名称中的“数字”,以下查询证明它们被分配了一个数值(如表中的NumericValue[v3-2].UnicodeCharacterDatabase所示:

SELECT 1 WHERE N'?' LIKE N'[3-5]'; -- HANGZHOU NUMERAL FOUR (U+3024)
-- 1
Run Code Online (Sandbox Code Playgroud)

但是,它们不是可用于数字运算的数字:

SELECT ? + 0;
/*
Msg 207, Level 16, State 1, Line 23
Invalid column name '?'.
*/
Run Code Online (Sandbox Code Playgroud)

关于解析和需要能够确定3e2是数字还是标识符的问题:虽然这是一个考虑因素,也可能是为什么数字被排除在“Ident_start”Unicode 一般类别之外,但它不是通用的,也不一定是为什么SQL Server 将它们排除在外。需要考虑的三点:

  1. 虽然3e2它本身是不明确的,但如果它至少有一个模式名称限定,那么它不会是:dbo.3e2
  2. 这个名字4aii一点也不含糊。内部解析将能够很容易地将其识别为不是潜在数字
  3. MySQL的/ MariaDB的没有这个限制。它们允许使用非分隔标识符,例如4aiiand 3e,但不允许使用3e2or 300。我能够在 MySQL 中成功执行以下操作:

    create table 4aii (3e int);
    
    Run Code Online (Sandbox Code Playgroud)

同样,您不能在 SQL Server 中执行此操作的原因是 SQL Server 遵守 Unicode 标准对标识符的建议。Unicode 联盟选择这些字符的原因没有具体说明,但似乎至少是“最佳实践”。尽管如此,正如 MySQL 所证明的那样,可以解析以数字开头的标识符。

  • @EvanCarroll Unicode 规范出于各种原因专门讨论了各种自定义。事实上,提到它的部分,[1.2 自定义](http://unicode.org/reports/tr31/#Customization) 甚至声明:“_每个编程语言标准都有自己的标识符语法;不同的编程语言有不同的约定在标识符中使用某些字符,例如 $、@、# 和 _。_”。这 4 个示例字符与 SQL Server 中使用的 4 个自定义完全相同。所以是的,非常正确:-)。阅读我的博客文章,分类特定于 Unicode 3.2。 (2认同)

Eva*_*oll 14

您所观察的是实现的词法分析器规则。这是一个叫做词法分析的过程的一部分,这是一种说“理解事物”的奇特方式。理想情况下,这将遵守 SQL 规范 ( <identifier>) 中给出的规则。这些规则均由 Microsoft 作为规则标识符规则发布。如果您希望使用不规则标识符,您必须引用它们或将它们与其他标记(Tsql[]或双引号"")“分隔” ,以消除任何歧义语法的可能性。

一根绳子就是一根绳子,amirite?

不,以这个为例。

“不,以这个为例。”

那是一句话。但是,更重要的是那是 5 个字。您知道这是五个单词,因为空格很重要。如果您要解析主题、对象和声音以将其理解为指令,则您必须知道它是五个单词。


小智 7

一个简单的例子,

3e2
Run Code Online (Sandbox Code Playgroud)

那是字符串“3e2”吗?数字300?变量名?如果您指的是数字,而忘记了您3e2 = 500之前在脚本中所写的内容怎么办?

规则在那里,以便语法解析器可以理解您的意思。可能有像4aii您的问题中提到的那样不明确的例子- 但有一部分标签是不明确的。所以为了避免这种歧义,我们有这个规则。