I have a persisted computed column I am using to hold a DATETIME2
calculated from a FLOAT
time column ([Potentially Bad Time]). I am handling underflow and overflow of all of the components of the time using DATEADD
but currently am having to keep recalculating the same values throughout the formula because I can't figure out how to store them in a variable! The actual implementation also supports year, month and day but is 210 lines so here is a cut-down version using just time components
CREATE TABLE Sales
(
[Id] INT IDENTITY(1,1) NOT NULL,
[Potentially Bad Time] FLOAT NOT NULL,
CONSTRAINT PK_Sales PRIMARY KEY
(
Id ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE Sales
ADD [Time] AS
DATEADD
(
HOUR,
IIF
(
ROUND([Potentially Bad Time] / 10000, 0, 1) < 0,
--Subtracts underflowing hours.
ROUND([Potentially Bad Time] / 10000, 0, 1),
IIF
(
ROUND([Potentially Bad Time] / 10000, 0, 1) >= 24,
--Adds overflowing hours.
ROUND([Potentially Bad Time] / 10000, 0, 1) - 24,
0
)
),
DATEADD
(
MINUTE,
IIF
(
ROUND([Potentially Bad Time] / 100, 0, 1) - ROUND([Potentially Bad Time] / 10000, 0, 1) * 100 < 0,
--Subtracts underflowing minutes.
ROUND([Potentially Bad Time] / 100, 0, 1) - ROUND([Potentially Bad Time] / 10000, 0, 1) * 100,
IIF
(
ROUND([Potentially Bad Time] / 100, 0, 1) - ROUND([Potentially Bad Time] / 10000, 0, 1) * 100 >= 60,
--Adds overflowing minutes.
ROUND([Potentially Bad Time] / 100, 0, 1) - ROUND([Potentially Bad Time] / 10000, 0, 1) * 100 - 60,
0
)
),
DATEADD
(
SECOND,
--If the seconds value is greater than or equal to 60, this adds on the overflowing seconds to the relevant DATETIME2 component(s).
IIF
(
ROUND([Potentially Bad Time], 0, 1) - ROUND([Potentially Bad Time] / 100, 0, 1) * 100 < 0,
--Subtracts underflowing seconds.
ROUND([Potentially Bad Time], 0, 1) - ROUND([Potentially Bad Time] / 100, 0, 1) * 100,
IIF
(
ROUND([Potentially Bad Time], 0, 1) - ROUND([Potentially Bad Time] / 100, 0, 1) * 100 >= 60,
--Adds overflowing seconds.
ROUND([Potentially Bad Time], 0, 1) - ROUND([Potentially Bad Time] / 100, 0, 1) * 100 - 59,
0
)
),
DATEADD
(
MILLISECOND,
IIF
(
[Potentially Bad Time] - ROUND([Potentially Bad Time], 0, 1) < 0,
--Subtracts underflowing milliseconds.
[Potentially Bad Time] - ROUND([Potentially Bad Time], 0, 1),
IIF
(
[Potentially Bad Time] - ROUND([Potentially Bad Time], 0, 1) > 9999999,
--Adds overflowing milliseconds.
[Potentially Bad Time] - ROUND([Potentially Bad Time], 0, 1) - 9999999,
0
)
),
DATETIME2FROMPARTS
(
--Year (Fixed for brevity of example)
1990,
--Month (Fixed for brevity of example)
12,
--Day (Fixed for brevity of example).
31,
--Hour
IIF
(
ROUND([Potentially Bad Time] / 10000, 0, 1) < 0,
0,
IIF
(
ROUND([Potentially Bad Time] / 10000, 0, 1) >= 24,
23,
ROUND([Potentially Bad Time] / 10000, 0, 1)
)
),
--Minute
IIF
(
ROUND([Potentially Bad Time] / 100, 0, 1) - ROUND([Potentially Bad Time] / 10000, 0, 1) * 100 < 0,
0,
IIF
(
ROUND([Potentially Bad Time] / 100, 0, 1) - ROUND([Potentially Bad Time] / 10000, 0, 1) * 100 >= 60,
59,
ROUND([Potentially Bad Time] / 100, 0, 1) - ROUND([Potentially Bad Time] / 10000, 0, 1) * 100
)
),
--Second
IIF
(
--If the seconds value is less than 0, truncates it to 0.
ROUND([Potentially Bad Time], 0, 1) - ROUND([Potentially Bad Time] / 100, 0, 1) * 100 < 0,
0,
--If the seconds value is greater than or equal to 60, this truncates it at 59.
IIF
(
ROUND([Potentially Bad Time], 0, 1) - ROUND([Potentially Bad Time] / 100, 0, 1) * 100 >= 60,
59,
ROUND([Potentially Bad Time], 0, 1) - ROUND([Potentially Bad Time] / 100, 0, 1) * 100
)
),
--Millisecond
IIF
(
[Potentially Bad Time] - ROUND([Potentially Bad Time], 0, 1) < 0,
0,
IIF
(
[Potentially Bad Time] - ROUND([Potentially Bad Time], 0, 1) > 9999999,
9999999,
[Potentially Bad Time] - ROUND([Potentially Bad Time], 0, 1)
)
),
3
)
)
)
)
)
PERSISTED
How can I put, for example, the [Time] - ROUND([Time], 0, 1)
in a variable named @milliseconds
to save calculating it multiple times?
I've tried a Scalar Function so the parameter could effectively be the variable but then Persisted Computed columns don't support the use of Scalar Functions (and they're bad news for performance anyway!).
I've also tried DECLARE @milliseconds FLOAT = [Time] - ROUND([Time], 0, 1)
between the AS
and the formula, but that's a syntax error.