SQL temp date table generation - sql

I wonder is there a way to generate a temp table containing dates but using between, because I have to use such a construction.
between Convert(datetime, '2022-01-01T00:00:00.000', 126) and Convert(datetime, '2022-03-04T23:59:59.998', 126)
I mean it should use between not StartDate,EndDate.

Another option which I think performs better than a recursive CTE
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT 0 AS I
UNION ALL
SELECT TOP (DATEDIFF(DAY, '20220101', '20220304'))
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2, N N3),
Dates AS(
SELECT DATEADD(DAY, T.I, '20220101') AS Date
FROM Tally T)
SELECT D.Date
into #tmpDates
FROM Dates D
EDIT
I always have a calendar table in my database, so I can just join on that. This performs quite well and the queries are much easier

An option for a getting the temp table. Not 100% it will do what's required. Hope this helps though.
DECLARE #start_date date = '2022-01-01',
#end_date date = '2022-03-04'
;WITH cte AS (
SELECT #start_date as DateRet
UNION ALL
SELECT CAST(DATEADD(day,1,dateRet) as date)
FROM cte
WHERE dateret < #end_date
)
SELECT *
into #tmpDates
FROM cte

Related

I want to create table or view from a recursion used to generate a date in ssms

I wrote a recursive query to generate a column pf dates. I want the dates to be stored as a table in a db but can't seem to find a way.
declare #startdate date = '2014-01-01';
declare #enddate date = '2023-12-31';
with calendar as
(
select #startdate as [orderDate]
union all
select DATEADD(dd,1,[orderdate])
from calendar
where DATEADD(dd,1,[orderdate])<= #enddate
)
select * from calendar
option (maxrecursion 0);
you can try this one to fill a new table your_table with the dates.
You can use that as a basis for your further operations.
WITH x AS (SELECT n FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) v(n))
select
convert(date, dat ) Dat
into your_table
from
(
SELECT top 100 percent
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) Line,
dateadd(day, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), '2014-01-01') Dat
FROM x ones, x tens, x hundreds, x thousands
ORDER BY 1
) basis
where dat <= '2023-12-31'

adding a row for missing data

Between a date range 2017-02-01 - 2017-02-10, i'm calculating a running balance.
I have days where we have missing data, how would I include these missing dates with the previous days balance ?
Example data:
we are missing data for 2017-02-04,2017-02-05 and 2017-02-06, how would i add a row in the query with the previous balance?
The date range is a parameter, so could change....
Can i use something like the lag function?
I would be inclined to use a recursive CTE and then fill in the values. Here is one approach using outer apply:
with dates as (
select mind as dte, mind, maxd
from (select min(date) as mind, max(date) as maxd from t) t
union all
select dateadd(day, 1, dte), mind, maxd
from dates
where dte < maxd
)
select d.dte, t.balance
from dates d outer apply
(select top 1 t.*
from t
where t.date <= d.dte
order by t.date desc
) t;
You can generate dates using tally table as below:
Declare #d1 date ='2017-02-01'
Declare #d2 date ='2017-02-10'
;with cte_dates as (
Select top (datediff(D, #d1, #d2)+1) Dates = Dateadd(day, Row_Number() over (order by (Select NULL))-1, #d1) from
master..spt_values s1, master..spt_values s2
)
Select * from cte_dates left join ....
And do left join to your table and get running total
Adding to the date range & CTE solutions, I have created Date Dimension tables in numerous databases where I just left join to them.
There are free scripts online to create date dimension tables for SQL Server. I highly recommend them. Plus, it makes aggregation by other time periods much more efficient (e.g. Quarter, Months, Year, etc....)

Auto generating dates based on a table

I have a table in SQL server
And I want to generate each presentation day between StartDate and EndDate. Normally, I have to create a script, declare a cursor and loop through the cursor to create each individual date. But using cursor slows thing down considerably.
I wonder if anyone has a better idea using join
I am successful in generating date based on a startdate and enddate
SELECT d."CalendarDay" AS "PresenttionDate",
DATEPART(dw,d."CalendarDay") AS "PresentationDay"
FROM
(
SELECT StartDate-1+number AS "CalendarDay"
FROM master..spt_values
where type='P' and number<= DateDiff(day,StartDate,EndDate)
)d
I just do not know how to tie the StartDate and EndDate to the presentation table.
Basically, I am looking for the end results below:
without involving cursor. Is that possible?
Please advise.
I think this is sufficient:
with n as (
select row_number() over (order by (select null)) - 1 as n
from master..spt_values
)
select t.*, dateadd(day, n.n, t.startDate) as thedate
from t join
n
on dateadd(day, n.n, t.startDate) <= t.endDate;

Find missing date as compare to calendar

I am explain problem in short.
select distinct DATE from #Table where DATE >='2016-01-01'
Output :
Date
2016-11-23
2016-11-22
2016-11-21
2016-11-19
2016-11-18
Now i need to find out missing date a compare to our calender dates from year '2016'
i.e. Here date '2016-11-20' is missing.
I want list of missing dates.
Thanks for reading this. Have nice day.
You need to generate dates and you have to find missing ones. Below with recursive cte i have done it
;WITH CTE AS
(
SELECT CONVERT(DATE,'2016-01-01') AS DATE1
UNION ALL
SELECT DATEADD(DD,1,DATE1) FROM CTE WHERE DATE1<'2016-12-31'
)
SELECT DATE1 MISSING_ONE FROM CTE
EXCEPT
SELECT * FROM #TABLE1
option(maxrecursion 0)
Using CTE and get all dates in CTE table then compare with your table.
CREATE TABLE #yourTable(_Values DATE)
INSERT INTO #yourTable(_Values)
SELECT '2016-11-23' UNION ALL
SELECT '2016-11-22' UNION ALL
SELECT '2016-11-21' UNION ALL
SELECT '2016-11-19' UNION ALL
SELECT '2016-11-18'
DECLARE #DATE DATE = '2016-11-01'
;WITH CTEYear (_Date) AS
(
SELECT #DATE
UNION ALL
SELECT DATEADD(DAY,1,_Date)
FROM CTEYear
WHERE _Date < EOMONTH(#DATE,0)
)
SELECT * FROM CTEYear
WHERE NOT EXISTS(SELECT 1 FROM #yourTable WHERE _Date = _Values)
OPTION(maxrecursion 0)
You need to generate the dates and then find the missing ones. A recursive CTE is one way to generate a handful of dates. Another way is to use master..spt_values as a list of numbers:
with n as (
select row_number() over (order by (select null)) - 1 as n
from master..spt_values
),
d as (
select dateadd(day, n.n, cast('2016-01-01' as date)) as dte
from n
where n <= 365
)
select d.date
from d left join
#table t
on d.dte = t.date
where t.date is null;
If you are happy enough with ranges of missing dates, you don't need a list of dates at all:
select date, (datediff(day, date, next_date) - 1) as num_missing
from (select t.*, lead(t.date) over (order by t.date) as next_date
from #table t
where t.date >= '2016-01-01'
) t
where next_date <> dateadd(day, 1, date);

Nest With clause inside a select statement

I have a recursive query using Common Table Expressions which gets the range of dates between a start and end date
WITH T(date) AS (
SELECT #StartDate UNION ALL
SELECT DateAdd(day,1,T.date) FROM T WHERE datediff(dd,T.date , #EndDate)>0 )
SELECT date FROM T OPTION (MAXRECURSION 32767))
Is there any way for me to nest this within another select statement without creating a temporary table?
I'm looking for a statement like so
select * from (WITH T(date) AS (
SELECT #StartDate UNION ALL
SELECT DateAdd(day,1,T.date) FROM T WHERE datediff(dd,T.date , #EndDate)>0 )
SELECT date FROM T OPTION (MAXRECURSION 32767)))
join
(select * from SomeTable where MyDate between #StartDate and #EndDate)
on //Some condition
I've tried this out in SQL Server and there is an
Incorrect Syntax near WITH
error being thrown.
By definition, CTE only exists within the scope of the query. So, is it necessary that a Temporary table is necessary to store the results of the CTE or can the above scenario also work?
You can use multiple CTEs by separating them with a comma, e.g:
WITH T(date) AS
(
SELECT #StartDate
UNION ALL
SELECT DateAdd(day,1,T.date)
FROM T
WHERE datediff(dd,T.date , #EndDate)>0
), T2 AS
(
SELECT date
FROM T
OPTION (MAXRECURSION 32767)
)
select * from t2
join
(select * from SomeTable where MyDate between #StartDate and #EndDate)
on //Some condition
For what it's worth though, using a recursive CTE to generate a list of dates is not the best way. The best way is to have a static calendar table, failing this you can generate a set of dates on the fly as follows:
SELECT TOP (DATEDIFF(DAY, #StartDate, #EndDate) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #StartDate)
FROM sys.all_objects a
CROSS JOIN sys.all_objects b;
This will be more efficient than looping through dates. For more information see:
Generate a set or sequence without loops – part 1
Generate a set or sequence without loops – part 2
Generate a set or sequence without loops – part 3
Create the CTE first, then select from it.
;WITH T(date) AS (
SELECT #StartDate UNION ALL
SELECT DateAdd(day,1,T.date) FROM T WHERE datediff(dd,T.date , #EndDate)>0
)
select * from T
join
(select * from SomeTable where MyDate between #StartDate and #EndDate) v
on //Some condition
OPTION (MAXRECURSION 32767)
If you're trying to create a date range, this may help : How to create a list of dates from a daterange without using a CTE