DATEDIFF(MINUTE, 0, <Date>) 中的 0 实际上是什么意思?

Jon*_*ite 4 datetime datetime2 sql-server-2016

因此,我们的数据团队寻求帮助以解决他们遇到的问题。我最终将其追踪到了一些真正超出范围的数据 (1/1/0001) 和他们使用的 DATEDIFF 函数。虽然我已经解决了他们的问题,但实际上我并不知道 0 在他们使用时会变成什么。

我最初认为它更接近整数溢出而不是真正的转换错误,但事实并非如此。我在带有 DATEDIFF_BIG 和相同错误的 SQL 2016 框中尝试了它。我有一个样本供你们在下面一起玩,以及哪些有效,哪些无效。

/** Setup The Sample */
DECLARE @TestValue DATETIME2(7)
SET @TestValue = '0001-01-01 10:30:00.0000000'

/** Conversion Error 
Msg 242, Level 16, State 3, Line 10
The conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value.
*/
SELECT DATEDIFF(MINUTE, 0, @TestValue)

--Also does not work, same error.
SELECT DATEDIFF_BIG(MINUTE, 0, @TestValue)

/** Works */
SELECT DATEDIFF(MINUTE, '1/1/1900', @TestValue)

/** Works */
SELECT DATEDIFF(MINUTE, CAST(0 AS DATETIME), @TestValue)

/** Doesn't Work, you can't cast 0 to a DATETIME2 */
--SELECT DATEDIFF(MINUTE, CAST(0 AS DATETIME2), @TestValue)

/** Works (or no error, which is fine)*/
SELECT DATEDIFF(MINUTE, 0, TRY_CAST(@TestValue AS DATETIME))
Run Code Online (Sandbox Code Playgroud)

额外问题,由于 0 不适用于 DATETIME2 的所有情况,有什么替代方法?

我们决定做什么

因此,我开始建议我的团队执行以下操作,因为您在许多 datemath 示例(每月的第一天等)中看到 0。因此,我建议您将 0 显式转换为日期时间,然后继续。这将在仍然工作时避免错误。所以:

DATEDIFF(MINUTE, CAST(DATETIME, 0), <Date>)
Run Code Online (Sandbox Code Playgroud)

Mar*_*ith 7

您可以通过将表达式添加到带有FROM子句的查询并查看计算标量来了解发生了什么。

这显示了以下内容。

+---------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------+
|                          Expression                           |                                                       Evaluated As                                                        |
+---------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------+
| DATEDIFF(MINUTE, 0, @TestValue)                               | Scalar Operator(datediff(minute,'1900-01-01 00:00:00.000',CONVERT_IMPLICIT(datetime,[@TestValue],0)))                     |
| DATEDIFF_BIG(MINUTE, 0, @TestValue)                           | Scalar Operator(datediff_big(minute,'1900-01-01 00:00:00.000',CONVERT_IMPLICIT(datetime,[@TestValue],0)))                 |
| DATEDIFF(MINUTE, '1/1/1900', @TestValue)                      | Scalar Operator(datediff(minute,'1900-01-01 00:00:00.0000000 +00:00',CONVERT_IMPLICIT(datetimeoffset(7),[@TestValue],0))) |
| DATEDIFF(MINUTE, CAST(0 AS DATETIME), @TestValue)             | Scalar Operator(datediff(minute,'1900-01-01 00:00:00.000 +00:00',CONVERT_IMPLICIT(datetimeoffset(7),[@TestValue],0)))     |
| DATEDIFF(MINUTE, CAST('1900-01-01' AS DATETIME2), @TestValue) | Scalar Operator(datediff(minute,'1900-01-01 00:00:00.0000000 +00:00',CONVERT_IMPLICIT(datetimeoffset(7),[@TestValue],0))) |
| DATEDIFF(MINUTE, 0, TRY_CAST(@TestValue AS DATETIME))         | Scalar Operator(datediff(minute,'1900-01-01 00:00:00.000',TRY_CAST([@TestValue] AS datetime)))                            |
+---------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------+
Run Code Online (Sandbox Code Playgroud)

如果您将文字传递0给此函数,它将始终隐式转换为datetime.

铸造 an inttodatetime返回1900-01-01 + <int> daysso 1900-01-01

您的问题是第三个参数的数据类型被强制转换为什么。@TestValuedatetime2- 当您传递一个整数时,双方都会隐式转换为datetime.

'0001-01-01 10:30:00.0000000'超出范围,datetime因此错误。

在它成功的时候,参数都被强制转换为 datetimeoffset(7)