日期添加副作用?

问题描述

我在使用 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 问题涉及相同的问题。