给出以下字段:
Y INT, --Year.
M INT, --Month.
D INT, --Day.
T FLOAT --Hours (H), minutes (m), seconds (s) and milliseconds (x) (in the form HHmmss.xxx).
是否可以通过 尝试 将这些值转换为DATETIME2
,而不必先将它们转换为基于字符串的数据类型?如果尝试失败(例如,由于日期值过多(例如,2019年1月35日)),我希望返回NULL
。
如果我使用DATETIME2FROMPARTS
,则如果传递了任何无效的组件,它只会失败。
我试图避免将它们转换为字符串,因为尝试此折衷的解决方案的唯一原因是因为我的另一个解决方案的性能(实际上可以处理上溢和下溢)在大型数据库上的运行速度非常慢(未完成) 5小时之内!),所以我试图尝试更简单的方法来查看它是否可以提高将其添加为计算的持久性列的性能。
答案 0 :(得分:1)
这是我最终为此编写的定制解决方案。不幸的是,存在重复的逻辑,因为我在持久化的计算列中使用了该逻辑,因此无法使用变量或标量函数(用于将参数用作变量)。
DECLARE @Y INT = 2000, @M INT = 2, @D INT = 29, @T FLOAT = 135559.999
SELECT IIF
(
--If the Year is out-of-bounds.
@Y <= 0 OR @Y > 9999
--Or the Month is out-of-bounds.
OR @M <= 0 OR @M > 12
--Or the Day is out-of-bounds (Accounts for leap years).
OR @D <= 0 OR @D > DAY(EOMONTH(DATETIME2FROMPARTS(@Y, @M, 1, 0, 0, 0, 0, 3)))
--Or the Time is less than 0
OR @T < 0
--Or the Hour is out-of-bounds.
OR ROUND(@T / 10000, 0, 1) >= 24
--Or the Minute is out-of-bounds.
OR ROUND(@T / 100, 0, 1) - ROUND(@T / 10000, 0, 1) * 100 >= 60
--Or the Second is out-of-bounds.
OR ROUND(@T, 0, 1) - ROUND(@T / 100, 0, 1) * 100 >= 60,
--NULL is returned
NULL,
--Otherwise, the Date Time components are parsable into a DATETIME2.
DATETIME2FROMPARTS
(
--Year
@Y,
--Month
@M,
--Day
@D,
--Hour
ROUND(@T / 10000, 0, 1),
--Minute
ROUND(@T / 100, 0, 1) - ROUND(@T / 10000, 0, 1) * 100,
--Second
ROUND(@T, 0, 1) - ROUND(@T / 100, 0, 1) * 100,
--Millisecond (multiplied by 1000 to use the first 3 decimal places).
(@T - ROUND(@T, 0, 1)) * 1000,
--Precision (3 is specified since only 3 decimal places become part of the integer for the fraction parameter above).
3
)
)
如果您需要毫秒精度与小数点后3位(毫秒精度为x
的毫秒),请在上一行将3
更改为x
,将* 1000
更改为{{ 1}}。