mssql 30 minute time intervals beteen 2 datetime - sql-server

I have below query and i want to get datetime in 30 min intervals between 2 datetime. Basicly I got it, but is limitited and wouln't return al results if the timediff is over 24 hrs.
For example:
#DateTime1 = 24/11/2016 18:00:00
#DateTime2 = 25/11/2016 06:00:00
Result: (in format "dd-HH:mm")
24-18:00
24-18:30
24-19:00
24-19:30
24-20:00
...
...
25-05:00
25-05:30
25-06:00
What I've tried.
SELECT number, DATEADD(MINUTE, number, #DateTime1) AS DateTimeLine, DATEPART(DAY, DATEADD(MINUTE, number, #DateTime1)) AS Days, DATEPART(MONTH,
DATEADD(MINUTE, number, #DateTime1)) AS Months, DATEPART(YEAR, DATEADD(MINUTE, number, #DateTime1)) AS Years, DATEPART(HOUR, DATEADD(MINUTE,
number, #DateTime1)) AS Hours, DATEPART(MINUTE, DATEADD(MINUTE, number, #DateTime1)) AS Minute, CAST(DATEADD(MINUTE, number, #DateTime1)
AS DATE) AS Date, CAST(DATEADD(MINUTE, number, #DateTime1) AS TIME) AS Time
FROM master.dbo.spt_values
WHERE (type = 'P') AND (DATEPART(MINUTE, DATEADD(MINUTE, number, #DateTime1)) = 30 OR DATEPART(MINUTE, DATEADD(MINUTE, number, #DateTime1)) = 0) AND (DATEADD(MINUTE, number, #DateTime1) <= #DateTime2)
ORDER BY number

A tally table is a great way to deal with this type of thing. I keep one in a view to avoid using spt_values.
create View [dbo].[cteTally] as
WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select N from cteTally
Then your code becomes really simple too. A small amount of datemath and voila.
declare #DateTime1 datetime = '2016/11/24 18:00:00'
, #DateTime2 datetime = '2016/11/25 06:00:00'
select FORMAT(DATEADD(minute, (t.N - 1) * 30, #DateTime1), 'dd-HH:mm')
from cteTally t
where t.N <= (DATEDIFF(hour, #DateTime1, #DateTime2) * 2) + 1

I have a TVF which generates dynamic date/time ranges. It is faster than a recursive cte, and I think more flexible. You pass the date range, desired DatePart, and increment.
Declare #DateTime1 DateTime = '2016-11-24 18:00:00'
Declare #DateTime2 DateTime = '2016-11-25 06:00:00'
Select Format(RetVal,'dd-HH:mm') from [dbo].[udf-Range-Date](#DateTime1,#DateTime2,'MI',30)
Returns
24-18:00
24-18:30
24-19:00
24-19:30
24-20:00
24-20:30
24-21:00
24-21:30
24-22:00
24-22:30
24-23:00
24-23:30
25-00:00
....
25-04:30
25-05:00
25-05:30
25-06:00
The UDF if needed
CREATE FUNCTION [dbo].[udf-Range-Date] (#R1 datetime,#R2 datetime,#Part varchar(10),#Incr int)
Returns Table
Return (
with cte0(M) As (Select 1+Case #Part When 'YY' then DateDiff(YY,#R1,#R2)/#Incr When 'QQ' then DateDiff(QQ,#R1,#R2)/#Incr When 'MM' then DateDiff(MM,#R1,#R2)/#Incr When 'WK' then DateDiff(WK,#R1,#R2)/#Incr When 'DD' then DateDiff(DD,#R1,#R2)/#Incr When 'HH' then DateDiff(HH,#R1,#R2)/#Incr When 'MI' then DateDiff(MI,#R1,#R2)/#Incr When 'SS' then DateDiff(SS,#R1,#R2)/#Incr End),
cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cte2(N) As (Select Top (Select M from cte0) Row_Number() over (Order By (Select NULL)) From cte1 a, cte1 b, cte1 c, cte1 d, cte1 e, cte1 f, cte1 g, cte1 h ),
cte3(N,D) As (Select 0,#R1 Union All Select N,Case #Part When 'YY' then DateAdd(YY, N*#Incr, #R1) When 'QQ' then DateAdd(QQ, N*#Incr, #R1) When 'MM' then DateAdd(MM, N*#Incr, #R1) When 'WK' then DateAdd(WK, N*#Incr, #R1) When 'DD' then DateAdd(DD, N*#Incr, #R1) When 'HH' then DateAdd(HH, N*#Incr, #R1) When 'MI' then DateAdd(MI, N*#Incr, #R1) When 'SS' then DateAdd(SS, N*#Incr, #R1) End From cte2 )
Select RetSeq = N+1
,RetVal = D
From cte3,cte0
Where D<=#R2
)
/*
Max 100 million observations -- Date Parts YY QQ MM WK DD HH MI SS
Syntax:
Select * from [dbo].[udf-Range-Date]('2016-10-01','2020-10-01','YY',1)
Select * from [dbo].[udf-Range-Date]('2016-01-01','2017-01-01','MM',1)
*/

using a recursive Common Table Expression [CTE] is one pretty clean method. For the formatting I am showing FORMAT() from SQL-Server 2012+ you may consider using DATEPART etc to do it though as FORMAT() can have performance impact.
I do agree with #RossBush's comment if you do things like this a lot generating a calendar (dates) and a time dimensions is very helpful for these purposes.
DECLARE #DateTime1 DATETIME = '2016/11/24 18:00:00'
DECLARE #DateTime2 DATETIME = '2016/11/25 06:00:00'
;WITH cte30MinIncrements AS (
SELECT #DateTime1 as DT
UNION ALL
SELECT DATEADD(MINUTE,30,DT)
FROM
cte30MinIncrements
WHERE DATEADD(MINUTE,30,DT) <= #DateTime2
)
SELECT
*
,FORMAT(DT,'dd-HH:mm') as Formated
FROM
cte30MinIncrements

Please see if this works.
declare #DateTime1 DateTime = '2016-11-24 18:00:00'
declare #DateTime2 DateTime = '2016-11-25 18:00:00'
declare #Interval DateTime = #DateTime1
declare #vartmptable table(DT DateTime)
While (#Interval < #DateTime2)
begin
--select #Interval, FORMAT(#Interval,'dd-HH:mm')
insert into #vartmptable select #Interval
set #Interval = DATEADD(mi,30,#Interval)
end
select FORMAT(DT,'dd-HH:mm') from #vartmptable

What about this? You can use variables/ fixed values as necessary.
WITH CTE_Numbers
AS (
SELECT n = 1
UNION ALL
SELECT n + 1
FROM CTE_Numbers
WHERE n < 100
)
SELECT FORMAT(DATEADD(mi, n * 30, '2016/11/03'),'dd-HH:mm')
FROM CTE_Numbers

Related

How do I can divide a day in 30 minutes intervals in SQL server for a single date or multiple days like a month [duplicate]

I have below query and i want to get datetime in 30 min intervals between 2 datetime. Basicly I got it, but is limitited and wouln't return al results if the timediff is over 24 hrs.
For example:
#DateTime1 = 24/11/2016 18:00:00
#DateTime2 = 25/11/2016 06:00:00
Result: (in format "dd-HH:mm")
24-18:00
24-18:30
24-19:00
24-19:30
24-20:00
...
...
25-05:00
25-05:30
25-06:00
What I've tried.
SELECT number, DATEADD(MINUTE, number, #DateTime1) AS DateTimeLine, DATEPART(DAY, DATEADD(MINUTE, number, #DateTime1)) AS Days, DATEPART(MONTH,
DATEADD(MINUTE, number, #DateTime1)) AS Months, DATEPART(YEAR, DATEADD(MINUTE, number, #DateTime1)) AS Years, DATEPART(HOUR, DATEADD(MINUTE,
number, #DateTime1)) AS Hours, DATEPART(MINUTE, DATEADD(MINUTE, number, #DateTime1)) AS Minute, CAST(DATEADD(MINUTE, number, #DateTime1)
AS DATE) AS Date, CAST(DATEADD(MINUTE, number, #DateTime1) AS TIME) AS Time
FROM master.dbo.spt_values
WHERE (type = 'P') AND (DATEPART(MINUTE, DATEADD(MINUTE, number, #DateTime1)) = 30 OR DATEPART(MINUTE, DATEADD(MINUTE, number, #DateTime1)) = 0) AND (DATEADD(MINUTE, number, #DateTime1) <= #DateTime2)
ORDER BY number
A tally table is a great way to deal with this type of thing. I keep one in a view to avoid using spt_values.
create View [dbo].[cteTally] as
WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select N from cteTally
Then your code becomes really simple too. A small amount of datemath and voila.
declare #DateTime1 datetime = '2016/11/24 18:00:00'
, #DateTime2 datetime = '2016/11/25 06:00:00'
select FORMAT(DATEADD(minute, (t.N - 1) * 30, #DateTime1), 'dd-HH:mm')
from cteTally t
where t.N <= (DATEDIFF(hour, #DateTime1, #DateTime2) * 2) + 1
I have a TVF which generates dynamic date/time ranges. It is faster than a recursive cte, and I think more flexible. You pass the date range, desired DatePart, and increment.
Declare #DateTime1 DateTime = '2016-11-24 18:00:00'
Declare #DateTime2 DateTime = '2016-11-25 06:00:00'
Select Format(RetVal,'dd-HH:mm') from [dbo].[udf-Range-Date](#DateTime1,#DateTime2,'MI',30)
Returns
24-18:00
24-18:30
24-19:00
24-19:30
24-20:00
24-20:30
24-21:00
24-21:30
24-22:00
24-22:30
24-23:00
24-23:30
25-00:00
....
25-04:30
25-05:00
25-05:30
25-06:00
The UDF if needed
CREATE FUNCTION [dbo].[udf-Range-Date] (#R1 datetime,#R2 datetime,#Part varchar(10),#Incr int)
Returns Table
Return (
with cte0(M) As (Select 1+Case #Part When 'YY' then DateDiff(YY,#R1,#R2)/#Incr When 'QQ' then DateDiff(QQ,#R1,#R2)/#Incr When 'MM' then DateDiff(MM,#R1,#R2)/#Incr When 'WK' then DateDiff(WK,#R1,#R2)/#Incr When 'DD' then DateDiff(DD,#R1,#R2)/#Incr When 'HH' then DateDiff(HH,#R1,#R2)/#Incr When 'MI' then DateDiff(MI,#R1,#R2)/#Incr When 'SS' then DateDiff(SS,#R1,#R2)/#Incr End),
cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cte2(N) As (Select Top (Select M from cte0) Row_Number() over (Order By (Select NULL)) From cte1 a, cte1 b, cte1 c, cte1 d, cte1 e, cte1 f, cte1 g, cte1 h ),
cte3(N,D) As (Select 0,#R1 Union All Select N,Case #Part When 'YY' then DateAdd(YY, N*#Incr, #R1) When 'QQ' then DateAdd(QQ, N*#Incr, #R1) When 'MM' then DateAdd(MM, N*#Incr, #R1) When 'WK' then DateAdd(WK, N*#Incr, #R1) When 'DD' then DateAdd(DD, N*#Incr, #R1) When 'HH' then DateAdd(HH, N*#Incr, #R1) When 'MI' then DateAdd(MI, N*#Incr, #R1) When 'SS' then DateAdd(SS, N*#Incr, #R1) End From cte2 )
Select RetSeq = N+1
,RetVal = D
From cte3,cte0
Where D<=#R2
)
/*
Max 100 million observations -- Date Parts YY QQ MM WK DD HH MI SS
Syntax:
Select * from [dbo].[udf-Range-Date]('2016-10-01','2020-10-01','YY',1)
Select * from [dbo].[udf-Range-Date]('2016-01-01','2017-01-01','MM',1)
*/
using a recursive Common Table Expression [CTE] is one pretty clean method. For the formatting I am showing FORMAT() from SQL-Server 2012+ you may consider using DATEPART etc to do it though as FORMAT() can have performance impact.
I do agree with #RossBush's comment if you do things like this a lot generating a calendar (dates) and a time dimensions is very helpful for these purposes.
DECLARE #DateTime1 DATETIME = '2016/11/24 18:00:00'
DECLARE #DateTime2 DATETIME = '2016/11/25 06:00:00'
;WITH cte30MinIncrements AS (
SELECT #DateTime1 as DT
UNION ALL
SELECT DATEADD(MINUTE,30,DT)
FROM
cte30MinIncrements
WHERE DATEADD(MINUTE,30,DT) <= #DateTime2
)
SELECT
*
,FORMAT(DT,'dd-HH:mm') as Formated
FROM
cte30MinIncrements
Please see if this works.
declare #DateTime1 DateTime = '2016-11-24 18:00:00'
declare #DateTime2 DateTime = '2016-11-25 18:00:00'
declare #Interval DateTime = #DateTime1
declare #vartmptable table(DT DateTime)
While (#Interval < #DateTime2)
begin
--select #Interval, FORMAT(#Interval,'dd-HH:mm')
insert into #vartmptable select #Interval
set #Interval = DATEADD(mi,30,#Interval)
end
select FORMAT(DT,'dd-HH:mm') from #vartmptable
What about this? You can use variables/ fixed values as necessary.
WITH CTE_Numbers
AS (
SELECT n = 1
UNION ALL
SELECT n + 1
FROM CTE_Numbers
WHERE n < 100
)
SELECT FORMAT(DATEADD(mi, n * 30, '2016/11/03'),'dd-HH:mm')
FROM CTE_Numbers

sql - generate random date, exluding weekends

I would like to ask you for a help with the query. I´m trying to get list of random dates from 7.2.2016 - 14.2.2016, but I would like to exclude from it 11. and 12.2.2016, which will be weekends.
This is, what I have:
SELECT DATEADD(DAY, ABS(CHECKSUM(NEWID()) % 8), '2016-02-07')
I´m using SQL server 2016.
and the dates should be allocated randomly to other columns:
enter image description here
SMS_send_day should be those dates, excluding 11.2. and 12.2.
Thank you for your advices!
Not sure if this helps, but this gets you a single date excluding the two you asked for. I assume you meant 2017, because those days weren't weekends in 2016.
declare #date date
while 1=1
begin
select #date = DATEADD(DAY, ABS(CHECKSUM(NEWID()) % 8), '2017-02-07')
if #date not in ('2/11/2017', '2/12/2017')
break
end
select #date
Edit Saw the comment...If a list is needed you can try something like this:
t-sql get all dates between 2 dates and then just use a where clause to filter out the weekend dates.
A Calendar or Tally table would do the trick, but I often use a TVF to create dynamic date/time ranges. It is parameter driven, you define the Date/Time Range, DatePart and Increment
Example
Declare #D1 date = '2016-02-07'
Declare #D2 date = '2016-02-14'
Select Top 1 D=RetVal
From [dbo].[udf-Range-Date](#D1,#D2,'DD',1)
Where DatePart(DW,RetVal) not in (7,1)
Order By NewID()
Returns
D
2016-02-11 00:00:00.000
The UDF if Interested
CREATE FUNCTION [dbo].[udf-Range-Date] (#R1 datetime,#R2 datetime,#Part varchar(10),#Incr int)
Returns Table
Return (
with cte0(M) As (Select 1+Case #Part When 'YY' then DateDiff(YY,#R1,#R2)/#Incr When 'QQ' then DateDiff(QQ,#R1,#R2)/#Incr When 'MM' then DateDiff(MM,#R1,#R2)/#Incr When 'WK' then DateDiff(WK,#R1,#R2)/#Incr When 'DD' then DateDiff(DD,#R1,#R2)/#Incr When 'HH' then DateDiff(HH,#R1,#R2)/#Incr When 'MI' then DateDiff(MI,#R1,#R2)/#Incr When 'SS' then DateDiff(SS,#R1,#R2)/#Incr End),
cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cte2(N) As (Select Top (Select M from cte0) Row_Number() over (Order By (Select NULL)) From cte1 a, cte1 b, cte1 c, cte1 d, cte1 e, cte1 f, cte1 g, cte1 h ),
cte3(N,D) As (Select 0,#R1 Union All Select N,Case #Part When 'YY' then DateAdd(YY, N*#Incr, #R1) When 'QQ' then DateAdd(QQ, N*#Incr, #R1) When 'MM' then DateAdd(MM, N*#Incr, #R1) When 'WK' then DateAdd(WK, N*#Incr, #R1) When 'DD' then DateAdd(DD, N*#Incr, #R1) When 'HH' then DateAdd(HH, N*#Incr, #R1) When 'MI' then DateAdd(MI, N*#Incr, #R1) When 'SS' then DateAdd(SS, N*#Incr, #R1) End From cte2 )
Select RetSeq = N+1
,RetVal = D
From cte3,cte0
Where D<=#R2
)
/*
Max 100 million observations -- Date Parts YY QQ MM WK DD HH MI SS
Syntax:
Select * from [dbo].[udf-Range-Date]('2016-10-01','2020-10-01','YY',1)
Select * from [dbo].[udf-Range-Date]('2016-01-01','2017-01-01','MM',1)
This will do the trick. I have used Date & Convert Functions.
DECLARE #i INT = 0
WHILE (#i<=7)
BEGIN
DECLARE #T TABLE
(
[DATE] DATE
)
INSERT INTO #T SELECT CONVERT (DATE,DATEADD(DAY,#i,'2016-02-07'))
SET #i = #i+1
IF(CONVERT (DATE,DATEADD(DAY,#i,'2016-02-07')) ='2016-02-11' OR CONVERT (DATE,DATEADD(DAY,#i,'2016-02-07')) ='2016-02-12')
SET #i = #i+2
END
SELECT * FROM #T
GO
Declare #i as int
Declare #dr as date
Set #i = 1
while #i < 8
Begin
SELECT #dr = DATEADD(DAY, #i, '2016-02-07')
while (((DATEPART(dw, #dr) + ##DATEFIRST) % 7) NOT IN (5, 6))
begin
print #dr
break;
end
Set #i = #i +1
End
go
If you get yourself a calendar table and you have a very simple query on your hands `
SELECT TOP 1 Date
FROM Calendar
WHERE IsWeekday = 1
AND Date >= #StartDate
AND Date <= #EndDate
ORDER BY NEWID();
You could always generate the dates on the fly though:
SET DATEFIRST 1;
DECLARE #Start DATE = '20160207',
#End DATE = '20160214';
WITH Calendar (Date) AS
( SELECT TOP (DATEDIFF(DAY, #Start, #End) + 1)
DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY N1.N) - 1, #Start)
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n1 (N)
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n2 (N)
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n3 (N)
)
SELECT TOP 1 Date
FROM Calendar
WHERE DATEPART(WEEKDAY, Date) NOT IN (6, 7)
ORDER BY NEWID();
Here the calendar CTE cross joins 3 table valued constructors to generate a maximum of 1,000 rows (10 x 10 x 10), then limits that to the number of days required using
TOP (DATEDIFF(DAY, #Start, #End) + 1)
Then generates a list of dates onward from the start by using ROW_NUMBER() to generate values from 1 to n. So the basic element is:
DECLARE #Start DATE = '20160207',
#End DATE = '20160214';
WITH Calendar (Date) AS
( SELECT TOP (DATEDIFF(DAY, #Start, #End) + 1)
DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY N1.N) - 1, #Start)
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n1 (N)
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n2 (N)
CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n3 (N)
)
SELECT Date
FROM Calendar
Which gives:
Date
------------
2016-02-07
2016-02-08
2016-02-09
2016-02-10
2016-02-11
2016-02-12
2016-02-13
It is then a simple case of removing weekends with WHERE DATEPART(WEEKDAY, Date) NOT IN (6, 7) and selecting a random row with TOP 1 ... ORDER BY NEWID(). As as aside, when using something setting sensitive like DATEPART(WEEKDAY, ...) you should always explicitly set the value you need rather than relying on defaults.
I may have misunderstood your requirements though, this last step is not necessary if you just want a list of all the dates
How about something like this:
SELECT inp.d AS [Start_Date]
,incr.r AS Increment
,DATEADD(DAY, incr.r, inp.d) AS New_Date
,FORMAT(DATEADD(DAY, incr.r, inp.d), 'ddd') AS New_Date_Weekday
FROM (
-- Input date goes here
SELECT CAST('2016-02-07' AS DATE) AS d
) inp
CROSS JOIN
(
-- Generate 7 rows
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 1)) - 1 AS r
FROM (VALUES(1),(1),(1),(1),(1),(1),(1)) v(n)
) incr
-- Convert week-day to 'Monday = 1 and Sunday = 7'-base;
-- Select a random week-day between 1 and 5
WHERE 1 + ( 5 + DATEPART(DW, DATEADD(DAY, incr.r, inp.d)) + ##DATEFIRST) % 7 = 1 + CAST(RAND() * 5 AS INT)
Thanks to Kakkarot for his answer explaining how to convert week-day count to be based on Monday = 1 and Sunday = 7.

T-SQL Generate months between dates from table

I have table with month's ranges like this:
CREATE TABLE [dbo].[T_month_ranges](
[StartMonth] [datetime] NULL,
[EndMonth] [datetime] NULL
) ON [PRIMARY]
INSERT INTO [dbo].[T_month_ranges] ([StartMonth],[EndMonth]) VALUES
('2015-02-01','2015-04-01')
INSERT INTO [dbo].[T_month_ranges] ([StartMonth],[EndMonth]) VALUES
('2016-12-01','2017-02-01')
INSERT INTO [dbo].[T_month_ranges] ([StartMonth],[EndMonth]) VALUES
('2017-08-01','2017-09-01');
These dates represents only year and month, first day is not important here.
Now we need to create select, that returns all months between dates in this table. So in the query result would be dates like these:
2015-02-01;2015-03-01;2015-04-01;2016-12-01;2017-01-01;2017-02-01;2017-08-01;2017-09-01
What is the best approach to achieve this in sql server ?
You can use a recursive CTE:
with m as (
select mr.startmonth as mon, mr.endmonth
from T_month_ranges mr
union all
select dateadd(month, 1, m.mon), m.endmonth
from m
where m.mon < m.endmonth
)
select m.mon
from m;
If your ranges are really wide (which seems unlikely with months), then you might need to set the MAXRECURSION option to 0.
An alternative -- which is probably faster -- is to use a table of numbers:
with n as (
select row_number() over (order by (select null)) - 1 as n
from master..spt_values
)
select dateadd(month, n.n, mr.startmonth)
from T_month_ranges mr join
n
on dateadd(month, n.n, mr.startmonth) <= mr.endmonth;
I'll often use a TVF to create dynamic date/time ranges. A tally/table would do the trick as well. The function is parameter driven, you supply the Range, DatePart, and Increment
For example:
Select A.*
,B.*
From T_month_ranges A
Cross Apply [dbo].[udf-Range-Date](A.StartMonth,A.EndMonth,'MM',1) B
Returns
The UDF if interested.
CREATE FUNCTION [dbo].[udf-Range-Date] (#R1 datetime,#R2 datetime,#Part varchar(10),#Incr int)
Returns Table
Return (
with cte0(M) As (Select 1+Case #Part When 'YY' then DateDiff(YY,#R1,#R2)/#Incr When 'QQ' then DateDiff(QQ,#R1,#R2)/#Incr When 'MM' then DateDiff(MM,#R1,#R2)/#Incr When 'WK' then DateDiff(WK,#R1,#R2)/#Incr When 'DD' then DateDiff(DD,#R1,#R2)/#Incr When 'HH' then DateDiff(HH,#R1,#R2)/#Incr When 'MI' then DateDiff(MI,#R1,#R2)/#Incr When 'SS' then DateDiff(SS,#R1,#R2)/#Incr End),
cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cte2(N) As (Select Top (Select M from cte0) Row_Number() over (Order By (Select NULL)) From cte1 a, cte1 b, cte1 c, cte1 d, cte1 e, cte1 f, cte1 g, cte1 h ),
cte3(N,D) As (Select 0,#R1 Union All Select N,Case #Part When 'YY' then DateAdd(YY, N*#Incr, #R1) When 'QQ' then DateAdd(QQ, N*#Incr, #R1) When 'MM' then DateAdd(MM, N*#Incr, #R1) When 'WK' then DateAdd(WK, N*#Incr, #R1) When 'DD' then DateAdd(DD, N*#Incr, #R1) When 'HH' then DateAdd(HH, N*#Incr, #R1) When 'MI' then DateAdd(MI, N*#Incr, #R1) When 'SS' then DateAdd(SS, N*#Incr, #R1) End From cte2 )
Select RetSeq = N+1
,RetVal = D
From cte3,cte0
Where D<=#R2
)
/*
Max 100 million observations -- Date Parts YY QQ MM WK DD HH MI SS
Syntax:
Select * from [dbo].[udf-Range-Date]('2016-10-01','2020-10-01','YY',1)
Select * from [dbo].[udf-Range-Date]('2016-01-01','2017-01-01','MM',1)
*/
Option 2 (without a UDF)
Select A.*
,B.*
From T_month_ranges A
Cross Apply (
Select Top (DateDiff(MM,A.Startmonth,A.EndMonth)+1)
Date=DateAdd(MM,Row_Number() over (Order by (Select null)) - 1,A.Startmonth)
From master..spt_values
) B

T-SQL - DateTime Gap Distribution

Code:
DECLARE #SD DATE = '2017-01-01'
,#ED DATE = '2017-01-07'
,#ST TIME = '08:00:00'
,#ET TIME = '16:00:00';
DECLARE #DT_T TABLE (SDT DATETIME, EDT DATETIME)
Goal:
To combine Start/End Date and Start/End Time (#SD/#ED and #ST/#ET) and create a table with gap intervals between StartDateTime and EndDateTime as shown in the desired output below.
Desired Output
/* #DT_T Data
SDT EDT
2017-01-01 08:00:00 2017-01-01 16:00:00
2017-01-02 08:00:00 2017-01-02 16:00:00
2017-01-03 08:00:00 2017-01-03 16:00:00
2017-01-04 08:00:00 2017-01-04 16:00:00
2017-01-05 08:00:00 2017-01-05 16:00:00
2017-01-06 08:00:00 2017-01-06 16:00:00
2017-01-07 08:00:00 2017-01-07 16:00:00
*/
I'm trying with the numbers table but so far not getting anywhere close.
Something like this? rextester: http://rextester.com/ULTV27021
declare #sd date = '2017-01-01'
,#ed date = '2017-01-07'
,#st_hour int = 8
,#et_hour int = 16;
declare #dt_t table (sdt datetime, edt datetime);
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
, d as (
select
sdt=dateadd(hour,#st_hour,convert(datetime,dateadd(day, row_number() over (order by (select 1)) -1,#sd)))
, edt=dateadd(hour,#et_hour,convert(datetime,dateadd(day, row_number() over (order by (select 1)) -1,#sd)))
from n as deka
cross join n as hecto
cross join n as kilo /* 2.73 years */
--cross join n as [10k] /* 27.3 years */
)
insert into #dt_t (sdt,edt)
select top (datediff(day,#sd,#ed)+1)
sdt
, edt
from d
order by sdt;
select * from #dt_t
note: rextester's default output formatting for dates is dd.MM.yyyy
Try this. Should get what you want.
DECLARE #SD DATE = '2017-01-01'
,#ED DATE = '2017-01-07'
,#ST TIME = '08:00:00'
,#ET TIME = '16:00:00';
DECLARE #DT_T TABLE (SDT DATETIME, EDT DATETIME)
Declare #i int = 0
While (#SD < #ED)
Begin
Insert Into #DT_T(SDT, EDT)
Select Cast(Convert(varchar(10),DateAdd(Day,#i,#SD),21) + ' ' + Convert(varchar(8),#ST,21) as datetime), Cast(Convert(varchar(10),DateAdd(Day,#i,#SD),21) + ' ' + Convert(varchar(10),#ET,21) as DateTime)
Set #i = #i + 1
IF (DateAdd(Day,#i,#SD) = #ED)
Break
Else
Continue
END
Insert Into #DT_T(SDT, EDT)
Select Cast(Convert(varchar(10),#ED,21) + ' ' + Convert(varchar(8),#ST,21) as datetime), Cast(Convert(varchar(10),#ED,21) + ' ' + Convert(varchar(10),#ET,21) as DateTime)
Select SDT, EDT From #DT_T
Use the next approach
use datediff function for inserting the rows between the 2
ranges.
update only the time in datetime values.
Demo:-
DECLARE #SD DATE = '2017-01-01'
,#ED DATE = '2017-01-07'
,#ST TIME = '08:00:00'
,#ET TIME = '16:00:00';
DECLARE #DT_T TABLE (SDT DATETIME, EDT DATETIME)
DECLARE #start int
set #start = 0
while #start <= datediff(day,#SD,#ED)
begin
insert into #DT_T values
( dateadd(day,#start, #SD) ,
dateadd(day,#start, #SD))
set #start = #start + 1
end
UPDATE #DT_T
SET SDT = DATEADD(HOUR, 8, CAST(CAST(SDT AS DATE) AS DATETIME)) ,
EDT = DATEADD(HOUR, 16, CAST(CAST(EDT AS DATE) AS DATETIME))
select * from #DT_T
Result:-
I'll often use a TVF to create dynamic date/time ranges. A tally table would do the trick as well. The UDF can be faster than a recursive CTE and is parameter driven. You supply the date range, DatePart and Increment. For Example:
Declare #D1 datetime = '2017-01-01'
Declare #D2 datetime = '2017-01-07'
Declare #T1 datetime = '08:00'
Select RetSeq
,STD = RetVal
,EDT = DateAdd(HOUR,8,RetVal)
From [dbo].[udf-Range-Date](#D1+#T1,#D2+#T1,'DD',1)
Returns
The UDF if needed
CREATE FUNCTION [dbo].[udf-Range-Date] (#R1 datetime,#R2 datetime,#Part varchar(10),#Incr int)
Returns Table
Return (
with cte0(M) As (Select 1+Case #Part When 'YY' then DateDiff(YY,#R1,#R2)/#Incr When 'QQ' then DateDiff(QQ,#R1,#R2)/#Incr When 'MM' then DateDiff(MM,#R1,#R2)/#Incr When 'WK' then DateDiff(WK,#R1,#R2)/#Incr When 'DD' then DateDiff(DD,#R1,#R2)/#Incr When 'HH' then DateDiff(HH,#R1,#R2)/#Incr When 'MI' then DateDiff(MI,#R1,#R2)/#Incr When 'SS' then DateDiff(SS,#R1,#R2)/#Incr End),
cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cte2(N) As (Select Top (Select M from cte0) Row_Number() over (Order By (Select NULL)) From cte1 a, cte1 b, cte1 c, cte1 d, cte1 e, cte1 f, cte1 g, cte1 h ),
cte3(N,D) As (Select 0,#R1 Union All Select N,Case #Part When 'YY' then DateAdd(YY, N*#Incr, #R1) When 'QQ' then DateAdd(QQ, N*#Incr, #R1) When 'MM' then DateAdd(MM, N*#Incr, #R1) When 'WK' then DateAdd(WK, N*#Incr, #R1) When 'DD' then DateAdd(DD, N*#Incr, #R1) When 'HH' then DateAdd(HH, N*#Incr, #R1) When 'MI' then DateAdd(MI, N*#Incr, #R1) When 'SS' then DateAdd(SS, N*#Incr, #R1) End From cte2 )
Select RetSeq = N+1
,RetVal = D
From cte3,cte0
Where D<=#R2
)
/*
Max 100 million observations -- Date Parts YY QQ MM WK DD HH MI SS
Syntax:
Select * from [dbo].[udf-Range-Date]('2016-10-01','2020-10-01','YY',1)
Select * from [dbo].[udf-Range-Date]('2016-01-01','2017-01-01','MM',1)
*/

How can I get first day of from YYYYWW in tsql

Can someone help me with getting the first and last day of week based on yearweek integer like 201648 without conserning about setting the ##firstdate attribute. I want iso date starting on monday in datetime format.
declare #yrwk int = 201648
declare #yr int = left(#yrwk,4)
declare #wk int = right(#yrwk,2)
select dateadd (week, #wk, dateadd (year, #yr-1900, 0)) - 4 - datepart(dw, dateadd (week, #wk, dateadd (year, #yr-1900, 0)) - 4) + 1
--returns 11/27/2016 which is Sunday of that week (start of week)
--change +1 to +2 at the end for "Monday"
After a little consideration, I thought that perhaps my dynamic Date/Time Range UDF may help here. I use this UDF to generate dynamic date/time ranges. You can supply the desired date range, date part and increment. A tally table would do the trick as well
In this case, we are getting the Nth Monday regardless of the datepart(WK,..) as per the requirements.
Declare #YYYYWW int = 201648
Select WkNbr = B.RetSeq
,WkBeg = B.RetVal
,WkEnd = DateAdd(DD,6,B.RetVal)
From (
Select MinDate=Min(RetVal)
From [dbo].[udf-Range-Date](DateFromParts(Left(#YYYYWW,4),1,1),DateFromParts(Left(#YYYYWW,4),1,10),'DD',1)
Where DateName(DW,RetVal)='Monday'
) A
Cross Apply (Select * From [dbo].[udf-Range-Date](A.MinDate,DateFromParts(Left(#YYYYWW,4),12,31),'DD',7) ) B
Where B.RetSeq = Right(#YYYYWW,2)
Returns
WkNbr WkBeg WkEnd
48 2016-11-28 2016-12-04
The UDF if interested
CREATE FUNCTION [dbo].[udf-Range-Date] (#R1 datetime,#R2 datetime,#Part varchar(10),#Incr int)
Returns Table
Return (
with cte0(M) As (Select 1+Case #Part When 'YY' then DateDiff(YY,#R1,#R2)/#Incr When 'QQ' then DateDiff(QQ,#R1,#R2)/#Incr When 'MM' then DateDiff(MM,#R1,#R2)/#Incr When 'WK' then DateDiff(WK,#R1,#R2)/#Incr When 'DD' then DateDiff(DD,#R1,#R2)/#Incr When 'HH' then DateDiff(HH,#R1,#R2)/#Incr When 'MI' then DateDiff(MI,#R1,#R2)/#Incr When 'SS' then DateDiff(SS,#R1,#R2)/#Incr End),
cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cte2(N) As (Select Top (Select M from cte0) Row_Number() over (Order By (Select NULL)) From cte1 a, cte1 b, cte1 c, cte1 d, cte1 e, cte1 f, cte1 g, cte1 h ),
cte3(N,D) As (Select 0,#R1 Union All Select N,Case #Part When 'YY' then DateAdd(YY, N*#Incr, #R1) When 'QQ' then DateAdd(QQ, N*#Incr, #R1) When 'MM' then DateAdd(MM, N*#Incr, #R1) When 'WK' then DateAdd(WK, N*#Incr, #R1) When 'DD' then DateAdd(DD, N*#Incr, #R1) When 'HH' then DateAdd(HH, N*#Incr, #R1) When 'MI' then DateAdd(MI, N*#Incr, #R1) When 'SS' then DateAdd(SS, N*#Incr, #R1) End From cte2 )
Select RetSeq = N+1
,RetVal = D
From cte3,cte0
Where D<=#R2
)
/*
Max 100 million observations -- Date Parts YY QQ MM WK DD HH MI SS
Syntax:
Select * from [dbo].[udf-Range-Date]('2016-10-01','2020-10-01','YY',1)
Select * from [dbo].[udf-Range-Date]('2016-01-01','2017-01-01','MM',1)
*/

Resources