【问题标题】:Parsing SQL Server number literal with underscore使用下划线解析 SQL Server 数字文字
【发布时间】:2016-03-14 12:51:13
【问题描述】:

我想知道为什么它会起作用以及为什么它不返回错误:

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'.

LiveDemo

【问题讨论】:

    标签: sql sql-server tsql sql-server-2012


    【解决方案1】:

    SQL 将查询视为

    SELECT 2015_11
    

    作为

    SELECT 2015 _11 
    

    这是

    的快捷方式
    SELECT 2015 AS [_11]
    

    SQL Server 要求列名遵循MSDN link 中详述的一些命名约定规则

    变量、函数和存储过程的名称必须符合以下 Transact-SQL 标识符规则。 第一个字符必须是以下之一:

    1. Unicode 标准 3.2 定义的字母。字母的 Unicode 定义包括从 a 到 z 的拉丁字符,从 A 到 Z,以及其他语言的字母字符。
    2. 下划线 (_)、at 符号 (@) 或数字符号 (#)。

      标识符开头的某些符号在 SQL Server 中具有特殊含义。以 at 开头的常规标识符 符号始终表示局部变量或参数,不能使用 作为任何其他类型对象的名称。开始的标识符 带有数字符号的表示临时表或过程。一个 以双数字符号 (##) 开头的标识符表示全局 临时对象。虽然数字符号或双数字符号 字符可用于开始其他类型对象的名称, 我们不推荐这种做法。

    某些 Transact-SQL 函数的名称以双 at 符号 (@@) 开头。为避免与这些功能混淆,您不应 使用以@@ 开头的名称。

    SELECT 的语法与 MSDN 类似

    选择 [ 全部 | DISTINCT ] [ TOP ( 表达式 ) [ PERCENT ] [ WITH TIES ] ] ::= { * | { 表名 |视图名称 |表别名}.* | { [ { 表名 |视图名称 |表别名}。 ] { 列名 | $身份 | $ROWGUID } | udt_column_name [ { . | :: } { { 属性名 |字段名} |方法名 ( 参数 [ ,...n] ) } ] |表达 [ [ AS ] column_alias ] } | column_alias = 表达式 } [ ,...n ]

    在这种情况下,SQL 解析器首先检查表名,然后检查列名、标识和行guid,等等,直到匹配到

    |表达式 [ [ AS ] column_alias ]

    然后它读取文字值直到下划线字符,当它意识到文字必须已经结束并开始将后面的字符解析为 Column_alias 而没有明确的AS

    要验证这一点,请尝试 SQL Server 中的以下代码

    SELECT 2015AS _11
    

    这将产生与

    相同的结果
    SELECT 2015_11
    

    还要验证我刚刚在上面写的内容,请查看 SSMS 的屏幕截图,该屏幕截图在 AS 上突出显示了代码

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

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

    SELECT 'a'_8
    

    这会给你类似的结果

    ╔═════╗
    ║ _8  ║
    ╠═════╣
    ║ a   ║
    ╚═════╝
    

    PS:您会发现这与 # 的工作方式几乎相同

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

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

    【讨论】:

    • 你能提供一些关于这种行为的文档吗?我同意可能是这种情况,但应该在某处提及。
    • 下划线触发扫描器的“下一个令牌”。即文字 2015 后跟(缺少空格),然后是 _11(有效的列别名。)
    【解决方案2】:

    要了解发生了什么,您需要了解 SQL Server 接受什么作为标识符。有很多规则,记录在here。但是,重要的是:

    第一个字符必须是以下之一:

    1. Unicode 标准 3.2 定义的字母。字母的 Unicode 定义包括从 a 到 z 的拉丁字符,从 A 到 Z,以及其他语言的字母字符。

    2. 下划线 (_)、at 符号 (@) 或数字符号 (#)。

    重要的一点是,当 SQL Server 解析器遇到一个数字时,它会对自己说:“这是一个数字”。当它碰到下划线时,它会说“好吧,没有更多的数字,必须开始别的东西”。解析器将第二个组件识别为有效标识符,因此将其视为:

    select 2015 _11
    

    这是一个列别名,即使没有as

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-01
      • 2016-07-25
      • 1970-01-01
      • 1970-01-01
      • 2021-02-18
      相关资源
      最近更新 更多