使用下划线解析SQL Server数字文字常量

9

我想知道为什么它可以工作,而且为什么不会返回错误:

SELECT 2015_11

结果:

╔══════╗
║ _11  ║
╠══════╣
║ 2015 ║
╚══════╝

第二种情况:
SELECT 2.1_a

╔═════╗
║ _a  ║
╠═════╣
║ 2.1 ║
╚═════╝

检查元数据:

SELECT  name, system_type_name
FROM sys.dm_exec_describe_first_result_set(
N'SELECT 2015_11', NULL, 0) 
UNION ALL
SELECT  name, system_type_name
FROM sys.dm_exec_describe_first_result_set(
N'SELECT 3.2_a', NULL, 0) 

╔══════╦══════════════════╗
║ name ║ system_type_name ║
╠══════╬══════════════════╣
║ _11  ║ int              ║
║ _a   ║ numeric(2,1)     ║
╚══════╩══════════════════╝

以字母开头的标识符表现得和我预期的一样:

SELECT a_11
-- Invalid column name 'a_11'.

实时演示

2个回答

9

SQL把查询看作是

SELECT 2015_11

作为

SELECT 2015 _11 

这是一个缩写,意思是

SELECT 2015 AS [_11]

SQL Server希望列名遵循详细说明在此MSDN链接中的命名约定规则。
变量、函数和存储过程的名称必须符合以下Transact-SQL标识符的规则。第一个字符必须是以下之一:
  1. Unicode Standard 3.2定义的字母,包括从a到z,从A到Z的拉丁字符以及其他语言的字母字符。
  2. 下划线(_),at符号(@)或井号(#).
在标识符开头的某些符号在SQL Server中具有特殊含义。始于at符号的常规标识符总是表示本地变量或参数,并且不能用作任何其他类型对象的名称。以数字符号开头的标识符表示临时表或过程。以双重数字符号(##)开头的标识符表示全局临时对象。虽然数字符号或双重数字符号字符可用于开始其他类型对象的名称,但我们不建议这样做。
一些Transact-SQL函数的名称以双at符号(@@)开头。为避免与这些函数混淆,不应使用以@@开头的名称。
此外,根据MSDN的语法,SELECT的语法如下:
SQL解析器首先检查表名,然后是列名、Identity和rowguid等,直到它与| expression [ [ AS ] column_alias ]匹配。然后读取下划线字符前的字面值,这时意识到字面值必须已经结束,并开始解析后面的字符作为没有显式ASColumn_alias
要验证此,请在SQL Server中尝试以下代码。
SELECT 2015AS _11

这将产生与以下结果相同的结果
SELECT 2015_11

同时,为了验证我刚才所写的,请参考SSMS的截图,它对AS进行了代码高亮。

enter image description here

在您的第一个示例中,2015是整数文字,而在第二个示例中,2.1是十进制文字。

在您的第三个示例中,a不是有效的文字。如果您尝试

SELECT 'a'_8

这将会给你类似以下的结果。
╔═════╗
║ _8  ║
╠═════╣
║ a   ║
╚═════╝

注意: 您会发现这个方法在使用 # 时同样有效

所以SELECT 2015#11也会给出类似的结果

╔══════╗
║ #11  ║
╠══════╣
║ 2015 ║
╚══════╝

你能提供一些关于这个行为的文档吗?我同意这可能是情况,但应该在某处提到。 - Lukasz Szozda
通过扫描仪下划线触发“下一个token”。即文字2015后跟(缺少空格),然后是_11(有效的列别名)。 - jarlh

3
为了理解正在发生的事情,您需要了解SQL Server接受什么作为标识符。有很多规则,这些规则在此处有记录。但是,重要的一点是:
第一个字符必须是以下之一: 1. Unicode标准3.2定义的字母。字母的Unicode定义包括从a到z、从A到Z的拉丁字符,以及其他语言的字母字符。 2. 下划线(_)、at符号(@)或井号(#)。
重要的一点是,当SQL Server解析器遇到数字时,它会对自己说:“这是一个数字”。当它遇到下划线时,它会说:“好吧,不再是数字了,肯定是开始其他东西了”。解析器将第二个组件识别为有效标识符,因此将其视为:
select 2015 _11

这是用于列别名的,甚至不需要使用 as


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接