因此,我们的数据团队寻求一些帮助来解决他们遇到的问题。我最终将其追踪到一些真正超出范围的数据(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))
额外的问题,因为 0 在 DATETIME2 的所有情况下都不起作用,还有什么替代方法?
我们决定做什么
因此,我开始建议我的团队执行以下操作,因为您在很多 datemath 示例(每月的第一天等)中看到 0。因此,我建议您将 0 显式转换为 datetime,然后继续。这将在仍然工作时避免错误。所以:
DATEDIFF(MINUTE, CAST(DATETIME, 0), <Date>)
您可以通过将表达式添加到带有
FROM
子句的查询并查看计算标量来了解发生了什么。这显示了以下内容。
如果您将文字传递
0
给此函数,它将始终隐式转换为datetime
.铸造 an
int
todatetime
返回1900-01-01 + <int> days
so1900-01-01
。您的问题是第三个参数的数据类型被转换为什么。
@TestValue
is ofdatetime2
- 当你传递一个整数时,双方都被隐式转换为datetime
.'0001-01-01 10:30:00.0000000'
datetime
因此错误超出范围。在它成功的时候,参数都被强制转换为
datetimeoffset(7)