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)
您可以通过将表达式添加到带有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 int
todatetime
返回1900-01-01 + <int> days
so 1900-01-01
。
您的问题是第三个参数的数据类型被强制转换为什么。@TestValue
是datetime2
- 当您传递一个整数时,双方都会隐式转换为datetime
.
'0001-01-01 10:30:00.0000000'
超出范围,datetime
因此错误。
在它成功的时候,参数都被强制转换为 datetimeoffset(7)