May 11, 2017 at 9:51 am
I'm basically trying to get Day-On-Day aggregates of some temporal tables I'm working with.
Basic task #1 is to get counts for each day. I've decided to use a date dimension table for this, hoping that it would make things easier.
Here's what I would assume is the obvious approach:SELECT
dd.[Date],
(SELECT COUNT(*) FROM computers FOR SYSTEM_TIME AS OF dd.[Date]) as 'ComputerCount'
FROM DateDimension dd
However, SQL Server doesn't care for this code. Throws an "Incorrect syntax near 'dd'." message at me.
Investigating this led me to create a distilled query set of what works and what doesn't with "SYSTEM_TIME AS OF":SELECT COUNT(*) FROM computers FOR SYSTEM_TIME AS OF '2017-05-06' -- works fine
SELECT COUNT(*) FROM computers FOR SYSTEM_TIME AS OF ('2017-05-06') -- syntax error
DECLARE @muhDate datetime = '2017-05-06'
SELECT COUNT(*) FROM computers FOR SYSTEM_TIME AS OF @muhDate -- also works fine!!!
Any thoughts? I've previously posted a similar topic which lead to dynamic SQL generation (which actually works), but I was thinking that this would be more sane...except for the fact that SQL doesn't seem to let anything other than a pre-built variable/string into that part of the syntax.
I'm basically trying to step up my day-on-day game without continuing to create dynamic UNION ALL queries for each day.
I can provide more information if necessary.
May 11, 2017 at 10:10 am
I suspect that dd is a reserved word. Try using [dd] instead. I just tried this with no problem, and it's basically the same construct as yours:SELECT
eh.*
, Nonsense =
(
SELECT COUNT(*)
FROM dbo.Emp
FOR SYSTEM_TIME AS OF '20150101'
)
FROM dbo.EmpHistory eh;
The absence of evidence is not evidence of absence.
Martin Rees
You can lead a horse to water, but a pencil must be lead.
Stan Laurel
May 11, 2017 at 11:15 am
The values for the system time can only be literals or variables. They cannot be date/time fields.
<system_time> ::=
{
AS OF <date_time>
| FROM <start_date_time> TO <end_date_time>
| BETWEEN <start_date_time> AND <end_date_time>
| CONTAINED IN (<start_date_time> , <end_date_time>)
| ALL
}<date_time>::=
<date_time_literal> | @date_time_variable<start_date_time>::=
<date_time_literal> | @date_time_variable<end_date_time>::=
<date_time_literal> | @date_time_variable
Drew
J. Drew Allen
Business Intelligence Analyst
Philadelphia, PA
May 11, 2017 at 11:17 am
Phil, I believe the key here is what you're allowed to put after the "AS OF".
"dd" is not a reserved word, as far as I know, and wrapping it in brackets doesn't change anything.
Of course the query you wrote is going to work; similar to my example, using a hard-coded data after "AS OF" is perfectly fine, but apparently using a SELECT'd date is not valid. I'm hoping there's a workaround for this.
For a test, try doing this:
SELECT
eh.*
, Nonsense =
(
SELECT COUNT(*)
FROM dbo.Emp
FOR SYSTEM_TIME AS OF ('2015' + '0101')
)
FROM dbo.EmpHistory eh;
or even just:
SELECT
eh.*
, Nonsense =
(
SELECT COUNT(*)
FROM dbo.Emp
FOR SYSTEM_TIME AS OF ('20150101')
)
FROM dbo.EmpHistory eh;
Maybe this helps to explain what I'm talking about.
It seems you can't pass anything as an expression to the "AS OF" syntax.
May 11, 2017 at 11:19 am
drew.allen - Thursday, May 11, 2017 11:15 AMThe values for the system time can only be literals or variables. They cannot be date/time fields.
<system_time> ::=
{
AS OF <date_time>
| FROM <start_date_time> TO <end_date_time>
| BETWEEN <start_date_time> AND <end_date_time>
| CONTAINED IN (<start_date_time> , <end_date_time>)
| ALL
}<date_time>::=
<date_time_literal> | @date_time_variable<start_date_time>::=
<date_time_literal> | @date_time_variable<end_date_time>::=
<date_time_literal> | @date_time_variable
Drew
Thanks, Drew.
This is the documentation I was looking for to confirm my suspicions. Didn't think to look in the FROM docs.
Any idea how I can work around this requirement outside of my current method of generating hundreds of UNION ALL queries?
May 11, 2017 at 11:45 am
Ah, sorry, I should have read your post more closely.
At least it gave me the opportunity to create my first temporal table, so thanks for that!
The absence of evidence is not evidence of absence.
Martin Rees
You can lead a horse to water, but a pencil must be lead.
Stan Laurel
September 10, 2018 at 1:05 am
Indeed, the syntax of the FOR SYSTEM_TIME AS OF clause allows only a constant or a variable, not a column.
To overcome this limitation, you can use a table-valued function to return the data from the temporary table at the time of the specified parameter, for example:
CREATE FUNCTION GetComputersCount(@DateTimeUTC datetime2(7))
RETURNS TABLE AS RETURN
SELECT COUNT(*) AS ComputerCount FROM computers FOR SYSTEM_TIME AS OF @DateTimeUTC
GO
SELECT dd.[Date],
(SELECT ComputerCount FROM dbo.GetComputersCount(dd.[Date])) as 'ComputerCount'
FROM DateDimension dd
September 10, 2018 at 4:42 pm
I'd probably use FOR SYSTEM_TIME BETWEEN xxx AND yyy and then JOIN that on your dimension table.
Viewing 8 posts - 1 through 7 (of 7 total)
You must be logged in to reply to this topic. Login to reply