Azure SQL中TIMESTAMP_MICROS(bigint)转datetime2遇算术溢出错误求助
问题根源&解决办法
这个Msg 8115算术溢出错误其实很好理解:你在DATEADD里把时间戳除以1e6后的结果强制转成了INT类型,但纽约出租车的行程数据里,肯定有不少2038年1月19日之后的记录——而INT类型的最大值是2147483647,对应的正好是1970-01-01加上这个秒数的时间,超过这个值的话,转成INT就会溢出。
之前SELECT TOP 1000能跑通,纯粹是运气好,抽样的1000条数据都没碰过2038年之后的记录;但加持久化计算列时,SQL Server会校验全表数据,只要有一条超范围的记录,就会触发这个错误。
解决思路分三种,按需选择:
1. 去掉多余的INT转换,用BIGINT计算秒数
SQL Server 2016及以后版本的DATEADD支持BIGINT作为间隔参数,所以只要把除法后的结果保持为BIGINT,不要强制转INT就行:
ALTER TABLE [dbo].[NYTaxi] ADD [pickup_datetime] AS CONVERT( datetime2, DATEADD(S, CAST([tpep_pickup_datetime] AS BIGINT) / 1000000, CONVERT(datetime2,'1970-01-01',120)) ) PERSISTED;
2. 直接用微秒级转换,更简洁准确
既然你的时间戳是微秒级的TIMESTAMP_MICROS,完全不用做除法,直接用DATEADD的microsecond参数一步到位,既避免了溢出,又保留了微秒精度:
ALTER TABLE [dbo].[NYTaxi] ADD [pickup_datetime] AS DATEADD(microsecond, CAST([tpep_pickup_datetime] AS BIGINT), CONVERT(datetime2,'1970-01-01')) PERSISTED;
3. 加上错误处理,非法时间戳返回NULL
要实现“格式错误返回NULL”的需求,用TRY_DATEADD和TRY_CONVERT组合就行——前者会在时间戳超出datetime2合法范围(比如早于1753年或晚于9999年)时返回NULL,后者确保最终结果要么是合法的datetime2,要么是NULL:
ALTER TABLE [dbo].[NYTaxi] ADD [pickup_datetime] AS TRY_CONVERT( datetime2, TRY_DATEADD(microsecond, CAST([tpep_pickup_datetime] AS BIGINT), CONVERT(datetime2,'1970-01-01')) ) PERSISTED;
验证问题根源(可选)
跑下面的查询,确认是不是真的有超过INT秒数上限的记录:
SELECT TOP 10 [tpep_pickup_datetime], [tpep_pickup_datetime]/1000000 AS seconds_since_epoch FROM [dbo].[NYTaxi] WHERE [tpep_pickup_datetime]/1000000 > 2147483647;
如果有返回结果,那就是这些记录导致的溢出,上面的解决办法都能搞定。
内容的提问来源于stack exchange,提问作者denzel
相关产品推荐
相关产品推荐

