附加的是在SQL中运行的代码示例。对于SQL Server,这似乎是意外行为。应该发生的是从数字中删除负数,但是在update命令下使用相同的功能时,它会确定绝对值并四舍五入。为什么会这样?
DECLARE @TEST TABLE (TEST varchar(2048));
INSERT INTO @TEST VALUES (' -29972.95');
SELECT TEST FROM @TEST;
SELECT ABS(TEST) FROM @TEST;
UPDATE @TEST SET TEST = ABS(TEST);
SELECT TEST FROM @TEST;
下面是该代码的结果。
-29972.95
29972.95
29973
答案 0 :(得分:3)
这似乎是CONVERT
函数的“功能”,而不是与SELECT
或UPDATE
有关的任何功能(唯一的不同是因为UPDATE
隐式转换FLOAT(8)
返回的ABS(...)
回到VARCHAR
)。
更新计划中的计算标量包含表达式
[Expr1003] = Scalar Operator(CONVERT_IMPLICIT(varchar(2048),
abs(CONVERT_IMPLICIT(float(53),[TEST],0))
,0) /*<-- style used for convert from float*/
)
值-输出
0(默认)-最多6位数字。适当时以科学计数法使用。
1-始终为8位数字。始终以科学计数法使用。
2-始终为16位数字。始终以科学计数法使用。
这可以在下面的示例中看到:
SELECT
[# Digits],
CONVERT(FLOAT(8), CONVERT(VARCHAR(20), N)) AS [FLOAT(VARCHAR(N))],
CONVERT(FLOAT(8), CONVERT(VARCHAR(20), N, 0)) AS [FLOAT(VARCHAR(N, 0))],
CONVERT(FLOAT(8), CONVERT(VARCHAR(20), N, 1)) AS [FLOAT(VARCHAR(N, 1))]
FROM (SELECT '6 digits', ABS('9972.95') UNION ALL SELECT '7 digits', ABS('29972.95')) T ([# Digits], N)
这将返回以下结果:
# Digits FLOAT(VARCHAR(N)) FLOAT(VARCHAR(N, 0)) FLOAT(VARCHAR(N, 1))
-------- ----------------- -------------------- --------------------
6 digits 9972.95 9972.95 9972.95
7 digits 29973 29973 29972.95
这证明UPDATE
以默认样式“ 0”有效地使用了CONVERT(VARCHAR, ABS(...))
。这将FLOAT
的范围从ABS
限制为6位数字。删除1个字符以免隐式转换溢出,在这种情况下,您将保留实际值。
将其返回给OP:
ABS
函数在示例中返回了FLOAT(8)
。UPDATE
然后导致隐式转换,该转换实际上是CONVERT(VARCHAR(2048),ABS(...),0),然后溢出了默认样式的最大位数。答案 1 :(得分:1)
(为简洁起见,删除了一些初步测试)
这肯定与INSERT / UPDATE期间的静默截断有关。
如果将值插入更改为此:
INSERT INTO @TEST SELECT ABS(' -29972.95')
您无需进行UPDATE即可立即获得相同的舍入/截断。
与此同时,SELECT ABS(' -29972.95')
产生了预期的结果。
进一步的测试支持隐式浮点转换的理论,并指出罪魁祸首是转换回varchar的原因:
DECLARE @Flt float = ' -29972.95'
SELECT @Flt;
SELECT CAST(@Flt AS varchar(2048))
产生:
-29972.95
-29972
可能是最终编辑:
我在嗅着和马丁一样的一棵树。 I found this。
这让我尝试了这个:
DECLARE @Flt float = ' -29972.95'
SELECT @Flt;
SELECT CONVERT(varchar(2048),@Flt,128)
哪个制作的:
-29972.95
-29972.95
因此,我将其称为
答案 2 :(得分:1)
ABS()
应该用于数字值,并且varchar输入将转换为float。对此行为最可能的解释是,浮点数在所有数字数据类型(例如十进制,整数,位)中都具有highest precedence。
您的SELECT
语句仅返回浮点结果。但是UPDATE
语句隐含地将浮点数转换回varchar并产生意外结果:
SELECT
test,
ABS(test) AS test_abs,
CAST(ABS(test) AS VARCHAR(100)) AS test_abs_str
FROM (VALUES
('-29972.95'),
('-29972.94'),
('-29972.9')
) AS test(test)
test | test_abs | test_abs_str
----------|----------|-------------
-29972.95 | 29972.95 | 29973
-29972.94 | 29972.94 | 29972.9
-29972.9 | 29972.9 | 29972.9
我建议您使用显式转换和精确的数值数据类型来避免隐式转换/浮点数的这种和其他潜在问题:
SELECT
test,
ABS(CAST(test AS DECIMAL(18, 2))) AS test_abs,
CAST(ABS(CAST(test AS DECIMAL(18, 2))) AS VARCHAR(100)) AS test_abs_str
FROM (VALUES
('-29972.95'),
('-29972.94'),
('-29972.9')
) AS test(test)
test | test_abs | test_abs_str
----------|----------|-------------
-29972.95 | 29972.95 | 29972.95
-29972.94 | 29972.94 | 29972.94
-29972.9 | 29972.90 | 29972.90
答案 3 :(得分:0)
ABS是一个数学函数,这意味着它被设计为使用数字值,在使用其他数据类型(例如VARCHAR)时,您不能期望该函数的正确行为,我建议首先对数字进行必要的CAST在应用ABS功能之前,请选择以下数据类型:
UPDATE @TEST SET TEST = ABS(CAST(TEST AS DECIMAL(18,2)))
此后,您的查询将输出
29972.95
这不能解决ABS在选择而不是在更新值时工作正常的可能性,这也许是sqlserver上的错误,但是避免转换为函数所需的正确数据类型确实是一种不好的做法。当执行SELECT子句但在UPDATE上被忽略时,可能会发生隐式强制转换,因为Microsoft期望您做正确的事。