问题描述
我在使用 Microsoft sql Server 2016 (SP2-CU15) 时遇到了一个非常奇怪的行为:
select convert(datetime,max(TS) + 1.0/24) as A
from table;
产量 2021-01-16 11:59:00.000
同时
select convert(datetime,max(TS) + 1.0/24) as A,dateadd(hour,1,max(TS)) as B
from table;
为 A 提供 2021-01-16 11:58:59.943
(为 B 提供 2021-01-16 11:59:00.000
)。所以,在我看来,添加第二列会改变第一列的结果?!
我可以通过将 1.0 转换为 real 来强制两列版本工作,顺便说一句:convert(datetime,max(TS) + cast(1.0 as real)/24)
,但我可以不通过编写 {{ 1}}。
知道这里发生了什么吗?
谢谢!
亨德里克。
更新:根据要求,这是一个最小的例子:
convert(datetime,max(TS) + cast(1.0 as float)/24)
如上所述,如果您注释掉 B 列,则 A 的值会发生变化。
解决方法
原因
感谢@MartinSmith 提供线索。
原因是查询自动参数化和选择的数据类型来存储值。
查询 1 是自动参数化的:
StatementText="SELECT CONVERT([datetime],MAX([TS])+@1/@2)
....
<ColumnReference Column="@2" ParameterCompiledValue="(24)" ParameterRuntimeValue="(24)" />
<ColumnReference Column="@1" ParameterCompiledValue="(1.0)" ParameterRuntimeValue="(1.0)" />
查询 2 未自动参数化:
StatementText="SELECT convert(datetime,max(TS) + 1.0/24) as A...."
为什么会发生是第一个查询而不是第二个查询,这有点像黑魔法。
来自 SQL Server data types 页:
当您使用 +、-、*、/ 或 % 算术运算符执行 int、smallint、tinyint 或 bigint 的隐式或显式转换 浮点数、实数、小数或数字数据类型的常量值, SQL Server 在计算数据类型和 表达式结果的精度取决于是否 查询是否自动参数化。
因此,查询中的相似表达式有时会产生
不同的结果。当查询未自动参数化时,常量
值首先转换为数字,其精度只是很大
足以保持常量的值,然后转换为
指定的数据类型。例如,常量值 1 转换为
numeric (1,0)
,并将常量值 250 转换为 numeric (3,0)
。
当查询被自动参数化时,常量值总是
在转换为最终数据之前转换为 numeric (10,0)
类型。当涉及到 / 操作符时,不仅结果类型的
类似查询的精度不同,但结果值可以
也不同。例如,自动参数化的结果值
包含表达式 SELECT CAST (1.0 / 7 AS float)
的查询
将不同于相同查询的结果值
自动参数化,因为自动参数化查询的结果
将被截断以适应 numeric (10,0)
数据类型。
效果
基于上述,使用了以下数据类型(有关如何计算结果类型的说明,请参阅:Precision,scale,and Length (Transact-SQL)):
查询 1 提供更高的精度:
NUMERIC( 2,1 ) / NUMERIC( 10,0 ) = NUMERIC( 13,12 )
查询 2:
NUMERIC( 2,1 ) / NUMERIC( 2,0 ) = NUMERIC( 7,6 )
解决方案
将您的文字和/或中间结果转换为所需的类型以避免意外。 在您的具体情况下,最好的解决方案是不要像 Panagiotis Kanavos 在他的 answer 中解释的那样使用数字算术来操纵日期。
或者,强制浮点数据类型(根据 Dan Guzman 评论)convert(datetime,max(TS) + 1e/24)
也可以解决问题。
This 问题涉及相同的问题。