在那里,我遇到了一个奇怪的行为:
SELECT 5 + '-' + 8
返回13
SELECT CAST('-' AS INT)
返回0,这就解释了以上的问题...
但是:为什么一个单破折号会被隐式转换为零?
顺便说一下:一个单加号或一个(多个)空字符串同样如此...
参见将破折号(-)转换为十进制数,但指出,将其转换为十进制数不会产生这些结果...
SELECT 5 + '-' + 8
返回13
SELECT CAST('-' AS INT)
返回0,这就解释了以上的问题...
但是:为什么一个单破折号会被隐式转换为零?
顺便说一下:一个单加号或一个(多个)空字符串同样如此...
参见将破折号(-)转换为十进制数,但指出,将其转换为十进制数不会产生这些结果...
这与正负数有关。我通过检查ISNUMERIC
得出了这个结论:
SELECT ISNUMERIC('-12') // Result: 1
SELECT ISNUMERIC('+12') // Result: 1
SELECT ISNUMERIC('.12') // Result: 1 (because "." can be cast to MONEY)
SELECT CAST('-' AS INT)
得到的是一个“负零”。即使SELECT CAST('-.' AS MONEY)
也是合法的。INT
类型只允许使用整数,不能在 INT
值中使用任何 .
。 - diiN__________SELECT CAST('.' AS DECIMAL(10,4))
,但是不起作用,而 SELECT CAST('.' AS MONEY)
确实可以。我认为你走在正确的道路上... - ShnugoSELECT CAST('.' AS DECIMAL(10,4))
是期望一个数字,因为你精确地定义了目标类型。'.0'
或 '0.'
没有问题。 - diiN__________由于内容过长/复杂,无法在评论中回答,因此我将其作为答案编写。请注意-我没有官方来源,因此不能确认我的逻辑是否被实施。(但我认为这很有意义 :))
但假设您正在编写一个转换函数,需要执行以下操作。
因此,您需要验证一个字符串-例如CAST('-50' AS INT);
然后逐个字符处理:
`-` is a valid part of the conversion, so move to next character.
`5` is a valid part of the conversion, so move to next character.
`0` is a valid part of the conversion, so move to next character.
Done.
假设有字符串 CAST('-' AS INT);
:
`-` is a valid part of the conversion, so move to next character.
Done.
现在 - 你可以进行额外的检查来使-
无效,因为它不能单独使用,但这需要额外的代码。
与+
和空格类似。以及货币和MONEY或期间和money也是如此:
SELECT CAST('$' AS MONEY)
SELECT CAST('.' AS MONEY)
这两个字符都是有效的 - 但实际上只能与实际数字一起使用。但它们可以被解析 - 这表明这是有意为之的,并且转换速度似乎很合理。
5 + '-'
5
是 INT
类型的字面量,'-'
是 CHAR(1)
类型的字面量。因为将 CHAR
隐式转换为 INT
比反过来高优先级,所以 +
将成为数字加法运算符而不是字符串连接运算符,这很不幸。
'-'
转换为INT
会产生0
,因为-
和+
都是合法的数字符号,并且允许一个没有数字的单个符号存在。为什么?嗯,因为它们是合法的。据我所知,这些规则在任何地方都没有得到记录。事实上,没有一条规则被记录下来。CONVERT(MONEY, ',,,')
会产生0
,因为,
完全被忽略为数字分隔符,即使它们在这里没有分隔任何数字。CONVERT(FLOAT, '+')
是不合法的,CONVERT(DECIMAL, '+')
也是如此,但CONVERT(INT, '+')
却没问题。Books Online只包含对行为的最简要引用。如果你反向工程实现这些行为的代码,无疑会发现一些古老而可疑的解析器快捷方式,现在它们已经保持兼容性。
请注意,我非常确定如果转换代码是今天编写的,微软会确保它会产生一个错误(就像使用,或大多数其他数字类型一样),因为结果是毫无意义的。但我同样确定,在野外存在着依赖于这种转换方式工作的代码的风险太大了。(想象一下所有使用“-”作为“不适用”值的文本文件,目前被导入为0,现在将因出现错误而中断。)
+
或-
。如果是,适当设置符号标志。现在处理字符串的其余部分。哎呀,没有剩余的部分,所以返回由符号修改的结果:0。(处理字符串的代码知道只允许数字:--
不会转换。)编写代码来检查我们是否实际处理了任何数字并在没有处理数字的情况下出错会更加复杂(不是多么复杂,但跟踪它的唯一原因是产生错误)。 - Jeroen MostertSELECT CAST('-' AS INT)
返回零... 性能相关的假设 足够幼稚 的解析器似乎是正确的。+1 给这个 足够幼稚 的解析器。让我微笑了一下... - Shnugo'-'
不允许用于 FLOAT
、REAL
或者 NUMERIC
(这些不需要性能吗?)。更多的是 '-'
由于代码结构的方式而成为有效字符串被简单地忽略了,而且今天没有人想要触碰这段代码,因为他们害怕会破坏东西。(添加了一个相关段落。) - Jeroen Mostert在这种情况下,默认情况下,SQL Server 尝试将表达式中的值转换为具有最高优先级的类型。Int 的优先级高于 char,因此 SQL Server 在此处隐式地将 '-'
转换为 int。这里有一份关于 SQL Server 类型优先级的文档。
SELECT 5 + '*' + 8
,你会遇到一个错误... - Shnugo+
或单个-
视为数字。可以讨论空字符串,但作为隐式转换,这就太过了...你不这么认为吗? - Shnugo
CAST('-5' AS INT)
是合法的时候,为了避免执行额外的代码来检查'-'
是否是转换的唯一部分并使转换无效,所以他们只是传递它。对于+
也是同样的道理。 - Allan S. Hansencast(' 6' AS int)
是合法的,因此为了避免检查是否只有空格,他们只允许转换通过,因为空格是一个合法字符。然而,我没有任何来源来支持它 - 它只是似乎是一个逻辑上的“快捷方式”来优化性能。 - Allan S. Hansen