I am not good in dealing with dates in SQL. I want to get full and partial day time slots between two dates in SQL Server.
Bit more explained.
For example, my date range is below:
StartDate - '2017-07-26 10:30:00'
End Date - '2017-07-29 16:45:00'
I want to populate time slots like below.
slot 1 - 2017-07-26 10:30:00 - 2017-07-26 23:59:00
slot 2 - 2017-07-27 00:00:00 - 2017-07-27 23:59:00
slot 3 - 2017-07-28 00:00:00 - 2017-07-28 23:59:00
slot 4 - 2017-07-29 00:00:00 - 2017-07-29 16:45:00
26 th and 29 th are not full days.
Does anyone have an idea how to achieve this in SQL Server? I can use a loop and do this but there will be performance issues. I tried with a recursive CTE, but it didn't work.
Thanks in advance. :)
This can be easily done using calendar table.. Here is one approach using Recursive CTE which generates dates on the fly
DECLARE #StartDate DATETIME = '2017-07-26 10:30:00',
#EndDate DATETIME = '2017-07-29 16:45:00';
WITH cte
AS (SELECT #StartDate st
UNION ALL
SELECT Cast(Cast(Dateadd(dd, 1, st) AS DATE) AS DATETIME)
FROM cte
WHERE st < CAST(#enddate AS DATE))
SELECT st,
CASE
WHEN #EndDate < Dateadd(mi, -1, Cast(Dateadd(dd, 1, Cast(st AS DATE)) AS DATETIME)) THEN #EndDate
ELSE Dateadd(mi, -1, Cast(Dateadd(dd, 1, Cast(st AS DATE)) AS DATETIME) )
END AS ed
FROM cte
I suggest you to create a physical calendar table to do this.
Rextester Demo
You can use CTE as below:
Declare #StartDate datetime = '2017-07-26 10:30:00'
Declare #EndDate datetime = '2017-07-29 16:45:00'
;with Cte_dates as (
Select dateadd(dd,1,Datediff(dd,0,#StartDate)) as StDate
Union All
Select DATEADD(dd,1, StDate) from Cte_dates where StDate < DAteADd(dd, -2, #endDate)
)
Select #StartDate as StartSlot, DateAdd(mi, -1,dateadd(dd,1,Datediff(dd,0,#StartDate))) as EndSlot
Union all
Select StDate as StartSlot,Dateadd(mi,-1, dateadd(dd,1,stDate)) EndSlot from Cte_dates --Generates from CTE for in between slots
Union all
Select Dateadd(dd,0,Datediff(dd,0,#EndDate)) as StartSlot, #EndDate as EndSlot
Output as below:
+-------------------------+-------------------------+
| StartSlot | EndSlot |
+-------------------------+-------------------------+
| 2017-07-26 10:30:00.000 | 2017-07-26 23:59:00.000 |
| 2017-07-27 00:00:00.000 | 2017-07-27 23:59:00.000 |
| 2017-07-28 00:00:00.000 | 2017-07-28 23:59:00.000 |
| 2017-07-29 00:00:00.000 | 2017-07-29 16:45:00.000 |
+-------------------------+-------------------------+
Related
I need help with my query to calculate the time until midnight between two date and time columns
break down by day
This is the main table:
ID
Start_Time
End_time
DateDiff
32221
01-01-2022 13:10:00
01-03-2022 13:10:00
2880
My query:
SELECT
start_time.ID,
start_time.Date_Time AS Start_time,
end_time.Date_Time AS End_time,
DATEDIFF(minute, start_time.Date_Time, end_time.Date_Time) AS DateDiff
FROM
Main
what I need is similar to this:
ID
Date_start
End_time
DateDiff
32221
01-01-2022 13:10:00
01-01-2022 23:59:59
654
32221
01-02-2022 00:00:00
01-02-2022 23:59:59
1440
32221
01-03-2022 00:00:00
01-03-2022 13:10:00
781
how i can do that?
You can loop through the times, always adding the time untill midnight, untill your 'start_time + 1 day' is bigger than your end_time.
The below code can be run directly in SQL (mind the date notation, my SQL is in united states notation, so if yours is in Europe this will give you back results for 3 months instead of 3 days);
DECLARE #start_time datetime2 = '01/01/2022 13:00:00';
DECLARE #end_time datetime2 = '03/01/2022 14:00:00';
DECLARE #daily_end_time datetime2=NULL;
DECLARE #Table Table (start_time datetime2, end_time datetime2, diff nvarchar(8));
DECLARE #diff_minutes_start int = DATEDIFF(MINUTE,#start_time,DateDiff(day,0,dateadd(day,1,#start_time)));
DECLARE #diff_minutes_end int = DATEDIFF(minute,#end_time,DateDiff(day,0,dateadd(day,1,#end_time)))
SET #daily_end_time = DATEADD(mi,#diff_minutes_start,#start_time)
WHILE #daily_end_time < #end_time
BEGIN
INSERT INTO #Table (start_time,end_time,diff)
VALUES (
#start_time,
CASE WHEN DATEADD(day,1,#daily_end_time) > #end_time THEN #end_time ELSE
#daily_end_time END,
CASE WHEN DATEADD(day,1,#daily_end_time) > #end_time THEN #diff_minutes_end ELSE
#diff_minutes_start END )
SET #daily_end_time = DATEADD(mi,#diff_minutes_start,#start_time)
SET #start_time = DATEADD(mi,1,#daily_end_time);
select #diff_minutes_start =
DATEDIFF(MINUTE,#start_time,DateDiff(day,0,dateadd(day,1,#start_time)));
select #diff_minutes_end = DATEDIFF(minute,#end_time,DateDiff(day,0,dateadd(day,1,#end_time)))
END
SELECT * FROM #Table
And the results:
You may use a recursive CTE as the following:
With CTE As
(
Select ID, Start_Time, End_time, DATEADD(Second, -1, DATEADD(Day, DATEDIFF(Day,0, Start_Time), 1)) et
From main
Union All
Select C.ID, DATEADD(Second, 1, C.et), C.End_time, DATEADD(Day, 1, C.et)
From CTE C Join main T
On C.ID = T.ID
Where DATEADD(Second, 1, C.et) <= C.End_time
)
Select ID, Start_Time,
Case When End_Time <= et Then End_Time Else et End As End_Time,
DATEDIFF(Minute, Start_Time, DATEADD(Second, 1, Case When End_Time <= et Then End_Time Else et End)) As [DateDiff]
From CTE
Order By ID, Start_Time
See a demo with extended data sample from db<>fiddle.
You can also solve this with a tally table, using the expanded (to show different cases) sample data
ID
StartTime
EndTime
32221
2022-01-01 13:10:00
2022-01-03 13:10:00
32222
2022-02-02 10:10:00
2022-02-02 17:10:00
32223
2022-03-03 19:10:00
2022-03-04 08:10:00
32224
2022-04-04 19:10:00
2022-04-08 08:10:00
and the code
with cteSampleData as ( --Enter some sample data, include spans of 0, 1, and >1 days
SELECT * --Note that we need CONVERT to make sure the dates are treated as datetime not string!
FROM (VALUES(32221, CONVERT(datetime2(0), '01-01-2022 13:10:00'), CONVERT(datetime2(0), '01-03-2022 13:10:00') )
, (32222, '02-02-2022 10:10:00', '02-02-2022 17:10:00')
, (32223, '03-03-2022 19:10:00', '03-04-2022 08:10:00')
, (32224, '04-04-2022 19:10:00', '04-08-2022 08:10:00')
) as Samp(ID, StartTime, EndTime)
), cteWithControl as ( --Add some fields to make testing cledarer - you could do this as part of a subsequent step instead
SELECT *
, CONVERT(date, StartTime) as StartDate , CONVERT(date, EndTime) as EndDate
, DATEDIFF(day, StartTime , EndTime) as DiffDays
--, DATEDIFF(day, CONVERT(date, StartTime) , CONVERT(date, EndTime)) as DiffDays
FROM cteSampleData
), cteTally as ( --Get a list of integers to represent days, assume nothing lasts longer than a year
SELECT top 365 ROW_NUMBER() over (ORDER BY name) as Tally
FROM sys.objects --just a table we know has over 300 rows, look up tally tables for other generation methods
)--The real work begins below, partition the data into "same day" and "multi-day" spans
, cteSet as (
SELECT ID, StartTime, EndTime, DiffDays, 1 as DayNumber
FROM cteWithControl WHERE DiffDays = 0
UNION ALL
SELECT ID --For multi-day, cross with the tally table and treat first and last days special
, CASE WHEN T.Tally = 1 THEN StartTime --For the first day the start time is the real time
ELSE DATEADD (day, T.Tally - 1, startdate) END as StartTime --Otherwise it's the start of the day
, CASE WHEN T.Tally = DiffDays + 1 THEN EndTime --For the last day the end is the real end
ELSE DATEADD (second, -1, CONVERT(DATETIME2(0), DATEADD (day, T.Tally, startdate)))
END as EndTime --otherwise 1 second less than the next day
, DiffDays, Tally as DayNumber
FROM cteWithControl as D CROSS JOIN cteTally as T
WHERE DiffDays > 0 AND T.Tally <= D.DiffDays + 1
)--Now we display the results and calculate the length (in minutes) of each span
SELECT *
, DATEDIFF(MINUTE, StartTime, EndTime) as DateDiff
FROM cteSet
ORDER BY ID, DayNumber
we get the output
ID
StartTime
EndTime
DiffDays
DayNumber
DateDiff
32221
2022-01-01 13:10:00
2022-01-01 23:59:59
2
1
649
32221
2022-01-02 00:00:00
2022-01-02 23:59:59
2
2
1439
32221
2022-01-03 00:00:00
2022-01-03 13:10:00
2
3
790
32222
2022-02-02 10:10:00
2022-02-02 17:10:00
0
1
420
32223
2022-03-03 19:10:00
2022-03-03 23:59:59
1
1
289
32223
2022-03-04 00:00:00
2022-03-04 08:10:00
1
2
490
32224
2022-04-04 19:10:00
2022-04-04 23:59:59
4
1
289
32224
2022-04-05 00:00:00
2022-04-05 23:59:59
4
2
1439
32224
2022-04-06 00:00:00
2022-04-06 23:59:59
4
3
1439
32224
2022-04-07 00:00:00
2022-04-07 23:59:59
4
4
1439
32224
2022-04-08 00:00:00
2022-04-08 08:10:00
4
5
490
I have a query getting the days in particular day that I selected and show the days in a month. My problem is that I need every two weeks of the month. And I also need to get long date format: dddd, dd MMMM yyyy and eliminate the time. Please help me.
DECLARE #startDate DATETIME = '9/1/2019'
DECLARE #endDate DATETIME = '12/31/2019'
DECLARE #dayOfWeek INT = 1;
WITH cte_Recursion AS
(
SELECT #startDate AS [Date]
UNION ALL
SELECT DATEADD(DAY, 1, [Date])
FROM cte_Recursion
WHERE [Date] < #endDate
)
SELECT [Date]
FROM cte_Recursion
WHERE DATEPART(WEEKDAY, [Date]) = #dayOfWeek
OPTION (MAXRECURSION 0)
The data returned looks like this:
2019-09-01 00:00:00.000
2019-09-08 00:00:00.000
2019-09-15 00:00:00.000
2019-09-22 00:00:00.000
2019-09-29 00:00:00.000
2019-10-06 00:00:00.000
2019-10-13 00:00:00.000
2019-10-20 00:00:00.000
2019-10-27 00:00:00.000
2019-11-03 00:00:00.000
2019-11-10 00:00:00.000
2019-11-17 00:00:00.000
2019-11-24 00:00:00.000
And I need to get like this (every other week):
2019-09-01 00:00:00.000
2019-09-15 00:00:00.000
2019-09-29 00:00:00.000
2019-10-13 00:00:00.000
2019-10-27 00:00:00.000
2019-11-10 00:00:00.000
2019-11-24 00:00:00.000
By changing your CTE to start on the correct day of the week, you can then change the recursion to add 14 days instead of 1, giving you the desired result without further manipulation:
DECLARE #startDate DATETIME = '9/1/2019'
DECLARE #endDate DATETIME = '12/31/2019'
DECLARE #dayOfWeek INT = 1;
WITH cte_Recursion AS
(SELECT DATEADD(DAY, ((#dayOfWeek - DATEPART(WEEKDAY, #startDate)) % 7 + 7) %7, #startDate) AS [Date]
UNION ALL SELECT DATEADD(DAY, 14, [Date])
FROM cte_Recursion
WHERE DATEADD(DAY, 14, [Date]) < #endDate)
SELECT [Date] FROM cte_Recursion
Output:
Date
01/09/2019 00:00:00
15/09/2019 00:00:00
29/09/2019 00:00:00
13/10/2019 00:00:00
27/10/2019 00:00:00
10/11/2019 00:00:00
24/11/2019 00:00:00
08/12/2019 00:00:00
22/12/2019 00:00:00
Demo on dbfiddle
To change the date format to dddd, dd MMMM yyyy, simply replace the final SELECT with:
SELECT FORMAT([Date], 'dddd, dd MMMM yyyy') AS [Date] FROM cte_Recursion
Output:
Date
Sunday, 01 September 2019
Sunday, 15 September 2019
Sunday, 29 September 2019
Sunday, 13 October 2019
Sunday, 27 October 2019
Sunday, 10 November 2019
Sunday, 24 November 2019
Sunday, 08 December 2019
Sunday, 22 December 2019
Demo on dbfiddle
you can use it :
DECLARE #startDate DATETIME = '9/1/2019'
DECLARE #endDate DATETIME = '12/31/2019'
DECLARE #dayOfWeek INT = 1;
WITH cte_Recursion AS
(
SELECT #startDate AS [Date]
UNION ALL SELECT DATEADD(DAY, 1, [Date])
FROM cte_Recursion WHERE [Date] < #endDate
)
SELECT [Date] FROM cte_Recursion
WHERE DATEPART(WEEKDAY, [Date]) = #dayOfWeek
and DATEPART(WEEK, [Date]) % 2 = 0
OPTION (MAXRECURSION 0)
You could use a recursive CTE as
WITH CTE AS
(
SELECT '2019-09-01 00:00:00.000' ADate
UNION SELECT '2019-09-08 00:00:00.000'
UNION SELECT '2019-09-15 00:00:00.000'
UNION SELECT '2019-09-22 00:00:00.000'
UNION SELECT '2019-09-29 00:00:00.000'
UNION SELECT '2019-10-06 00:00:00.000'
UNION SELECT '2019-10-13 00:00:00.000'
UNION SELECT '2019-10-20 00:00:00.000'
UNION SELECT '2019-10-27 00:00:00.000'
UNION SELECT '2019-11-03 00:00:00.000'
UNION SELECT '2019-11-10 00:00:00.000'
UNION SELECT '2019-11-17 00:00:00.000'
UNION SELECT '2019-11-24 00:00:00.000'
),
F AS
(
SELECT CAST('2019-09-01 00:00:00.000' AS DATETIME) ADate
UNION ALL
SELECT DATEADD(Day, 14, F.ADate)
FROM F
WHERE ADate < '2019-11-24 00:00:00.000'
)
SELECT *
FROM F;
I currently have an issue whereby I am doing DATEDIFF in minutes between a start date and end date, however I when this date goes over into a new month I need the figures to be separate for each month.
Please see example data (both Text and Image view);
SELECT [BookingNum]
,[StartDate]
,[EndDate]
,[Location]
,DATEPART(m,startdate) AS [Month]
,DATEDIFF(MINUTE,StartDate,EndDate) AS [Minutes]
FROM [Test].[dbo].[Booking]
BookingNum StartDate EndDate Location Month Minutes
1 2019-02-05 12:54:00.000 2019-02-08 15:00:00.000 Area 1 2 4446
2 2019-05-02 10:41:00.000 2019-05-10 12:39:00.000 Area 2 5 11638
3 2019-06-01 10:30:00.000 2019-06-04 09:25:00.000 Area 3 6 4255
4 2019-02-02 09:41:00.000 2019-04-20 11:54:00.000 Area 1 2 111013
5 2019-03-29 19:09:00.000 2019-04-02 10:41:00.000 Area 3 3 5252
For rows 4 & 5 there would need to be additional rows as they go across multiple months.
Example for the data in row 4, I would want to see;
StartDate EndDate Location Month Minutes
2019-02-02 09:41:00.000 2019-02-28 23:59:00.000 Area 1 2 38298
2019-03-01 00:00:00.000 2019-03-31 23:59:00.000 Area 1 3 44639
2019-04-01 00:00:00.000 2019-04-20 23:59:00.000 Area 1 4 28074
This would then give me the total minutes for that month only between the start and end date.
Any help much appreciated.
Edit: Recursive CTE should do the trick! Basically, use recursion to keep getting the start date through the lesser of the EOM and the end date, until ultimately you reach the end date.
Fiddle
DECLARE #tbl TABLE (bookingnum INT, sd DATETIME, ed DATETIME)
INSERT INTO #tbl VALUES
(1, '2/5/2019 12:54 PM', '2/8/2019 3:00 PM'),
(2, '5/2/2019 10:41 AM', '5/10/2019 12:39 PM'),
(3, '6/1/2019 10:30 AM', '6/4/2019 9:25 AM'),
(4, '2/2/2019 9:41 AM', '5/20/2019 11:54 AM'),
(5, '3/29/2019 7:09 PM', '4/2/2019 10:41 AM')
;WITH cte AS (
SELECT bookingnum, sd, DATEADD(DAY, 1, EOMONTH(sd)) eom, ed,
CASE WHEN DATEADD(DAY, 1, EOMONTH(sd)) < ed THEN DATEADD(DAY, 1, EOMONTH(sd)) else ed END AS applied_ed
FROM #tbl
UNION ALL
SELECT bookingnum, applied_ed, DATEADD(DAY, 1, EOMONTH(applied_ed)) eom, ed,
CASE WHEN DATEADD(DAY, 1, EOMONTH(applied_ed)) < ed THEN DATEADD(DAY, 1, EOMONTH(applied_ed)) else ed END AS applied_ed
FROM cte
WHERE applied_ed < ed
)
SELECT bookingnum, sd, applied_ed AS ed, DATEDIFF(MINUTE, sd, applied_ed) minutes
FROM cte
ORDER BY bookingnum, sd
Returns:
bookingnum sd ed minutes
1 2019-02-05 12:54:00.000 2019-02-08 15:00:00.000 4446
2 2019-05-02 10:41:00.000 2019-05-10 12:39:00.000 11638
3 2019-06-01 10:30:00.000 2019-06-04 09:25:00.000 4255
4 2019-02-02 09:41:00.000 2019-03-01 00:00:00.000 38299
4 2019-03-01 00:00:00.000 2019-04-01 00:00:00.000 44640
4 2019-04-01 00:00:00.000 2019-05-01 00:00:00.000 43200
4 2019-05-01 00:00:00.000 2019-05-20 11:54:00.000 28074
5 2019-03-29 19:09:00.000 2019-04-01 00:00:00.000 3171
5 2019-04-01 00:00:00.000 2019-04-02 10:41:00.000 2081
This can be achieved using recursive CTE as follows. This calculates multiple months between startdate and enddate.
Fiddle: http://sqlfiddle.com/#!18/26568/4
create table #temp(
BookingNum int,
StartDate datetime,
EndDate datetime,
Location varchar(25),
)
insert into #temp
values(1,'2019-02-05 12:54:00','2019-02-08 15:00:00','Area 1'),
(2,'2019-05-02 10:41:00','2019-05-10 12:39:00','Area 2'),
(3,'2019-06-01 10:30:00','2019-06-04 09:25:00','Area 3'),
(4,'2019-02-02 09:41:00','2019-05-20 11:54:00','Area 1'),
(5,'2019-03-29 19:09:00','2019-04-02 10:41:00','Area 3')
;WITH cte AS
(
SELECT BookingNum,
StartDate,
CASE
WHEN DATEPART(m, EndDate) > DATEPART(m, startdate)
THEN DATEADD(s, -1, DATEADD(mm, DATEDIFF(m, 0, startdate) + 1, 0))
ELSE EndDate
END AS EndDate,
Location,
DATEPART(m, EndDate) - DATEPART(m, startdate) AS MonthDiff
FROM #temp
UNION ALL
SELECT cte.BookingNum,
CASE
WHEN cte.MonthDiff > 0
THEN DATEADD(month, DATEDIFF(month, 0, DATEADD(month, 1, cte.StartDate)), 0)
ELSE cte.StartDate
END AS startDate,
CASE
WHEN cte.MonthDiff > 0 AND DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, DATEADD(month, 1, cte.StartDate)) + 1, 0)) < t.EndDate
THEN DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, DATEADD(month, 1, cte.StartDate)) + 1, 0))
ELSE t.EndDate
END AS EndDate,
cte.Location,
(cte.MonthDiff - 1) MonthDiff
FROM cte
INNER JOIN #temp t ON cte.BookingNum = t.BookingNum
WHERE cte.MonthDiff > 0
)
SELECT BookingNum,
StartDate,
EndDate,
Location,
DATEPART(m, startdate) AS month,
DATEDIFF(minute, startdate, enddate) AS minutes
FROM cte
ORDER BY 1;
drop table #temp
Result:
BookingNum StartDate EndDate Location month minutes
----------- ----------------------- ----------------------- ------------------------- ----------- -----------
1 2019-02-05 12:54:00.000 2019-02-08 15:00:00.000 Area 1 2 4446
2 2019-05-02 10:41:00.000 2019-05-10 12:39:00.000 Area 2 5 11638
3 2019-06-01 10:30:00.000 2019-06-04 09:25:00.000 Area 3 6 4255
4 2019-02-02 09:41:00.000 2019-02-28 23:59:59.000 Area 1 2 38298
4 2019-03-01 00:00:00.000 2019-03-31 00:00:00.000 Area 1 3 43200
4 2019-04-01 00:00:00.000 2019-04-30 00:00:00.000 Area 1 4 41760
4 2019-05-01 00:00:00.000 2019-05-20 11:54:00.000 Area 1 5 28074
5 2019-03-29 19:09:00.000 2019-03-31 23:59:59.000 Area 3 3 3170
5 2019-04-01 00:00:00.000 2019-04-02 10:41:00.000 Area 3 4 2081
To achieve this you will need to create an additional table to join to that contains the months. You would then join to that table where the month of the date is between the dates in the calendar table, to do this you need to use a dateadd/datediff function to round your date to the first of the month e.g.: DATEADD(month, DATEDIFF(month, 0, StartDate),0). This works by calculating the difference in months between some random start date (in this case 0, I.e. 1/1/1900) and then adding those months back on to the start date.
Then you will need to round your start or end date up or down to the end of the month if they are not in the same month as the calendar table record, which will allow you to do a new calculation for the time.
The whole code would look something like this:
CREATE TABLE #MonthDate
(MonthDate date PRIMARY KEY);
INSERT INTO #MonthDate (MonthDate)
VALUES ('20190101'),('20190201'),('20190301'),('20190401'),('20190501'),('20190601');
WITH RoundedDates As
(SELECT b.StartDate,
B.EndDate,
DATEADD(month, DATEDIFF(month, 0, b.StartDate),0) AS RoundedStartDate,
DATEADD(month, DATEDIFF(month, 0, b.EndDate),0) AS RoundedEndDate
FROM Test.dbo.Booking AS b)
SELECT rd.StartDate
, rd.EndDate
, DATEDIFF(minute, CASE WHEN rd.RoundedStartDate = md.MonthDate THEN rd.StartDate ELSE md.MonthDate END, CASE WHEN rd.RoundedEndDate = md.MonthDate THEN rd.EndDate ELSE DATEADD(month,1,md.MonthDate) END) AS Minutes
FROM RoundedDates AS rd
INNER JOIN #MonthDate as md
ON md.MonthDate BETWEEN rd.RoundedStartDate AND rd.RoundedEndDate
http://sqlfiddle.com/#!18/70730/2
My question is similar to this
Get a list of dates between two dates using a function
using this code for 10 days recurring:
Declare #startDate datetime
Declare #endDate datetime
set #startDate= '03/01/2019 12:00:00'
set #endDate = '04/30/2019 12:00:00'
;WITH mycte AS
(
SELECT CAST(#startDate AS DATETIME) DateValue
UNION ALL
SELECT DateValue + 10
FROM mycte
WHERE DateValue + 10 < #endDate - 1
)
SELECT DateValue
FROM mycte
OPTION (MAXRECURSION 0)
I get the ff result:
2019-03-20 12:00:00.000
2019-03-30 12:00:00.000
2019-04-09 12:00:00.000
2019-04-19 12:00:00.000
but I want the result to be :
2019-03-20 12:00:00.000
2019-03-30 12:00:00.000
2019-03-31 12:00:00.000
2019-04-09 12:00:00.000
2019-04-10 12:00:00.000
2019-04-20 12:00:00.000
2019-04-21 12:00:00.000
2019-04-30 12:00:00.000
Is this possible with SQL?
I find the problem rather arcane, but this seems to do what you want:
set #startDate= '2019-03-10 12:00:00';
set #endDate = '2019-04-30 12:00:00';
WITH mycte AS (
SELECT CAST(#startDate AS DATETIME) as DateValue
UNION ALL
SELECT CONVERT(DATETIME, EOMONTH(DateValue)) + CONVERT(DATETIME, CONVERT(TIME, DateValue))
FROM mycte
WHERE MONTH(DateValue + 10) <> MONTH(DateValue) AND
CONVERT(DATE, DATEVALUE) <> CONVERT(DATE, EOMONTH(DateValue)) AND
DateValue < #endDate
UNION ALL
SELECT DateValue + 10
FROM mycte
WHERE DateValue + 10 < #endDate
)
SELECT DISTINCT DateValue
FROM mycte
ORDER BY DateValue
OPTION (MAXRECURSION 0);
Here is a db<>fiddle.
I have a table with two columns
start_date 03/09/2016
end_date 03/15/2016
Now I need all the week start and end dates between these two dates
week_start_date week_end_date
03/07/2016 03/11/2016
03/14/2016 03/18/2016
How can I achieve this using sql query. No procedures or t-sql please.
first you have to find all dates between #start_date and #end_date :
declare #start_date datetime
declare #end_date datetime
set #start_date='03/09/2016'
set #end_date='03/15/2016'
;
WITH dates AS (
SELECT #start_date AS dt
UNION ALL
SELECT DATEADD(dd, 1, dt)
FROM dates s
WHERE DATEADD(dd, 1, dt) <= #end_date)
result:
2016-03-09 00:00:00.000
2016-03-10 00:00:00.000
2016-03-11 00:00:00.000
2016-03-12 00:00:00.000
2016-03-13 00:00:00.000
2016-03-14 00:00:00.000
2016-03-15 00:00:00.000
second you need to find out weekday ,'2' is Monday week_start_date
'6' is friday week_end_date
select DATEPART(dw,'03/11/2016') -- friday =6
select DATEPART(dw,'03/14/2016') --- monday =2
here is the final query:
declare #start_date datetime
declare #end_date datetime
set #start_date='03/01/2016'
set #end_date='03/31/2016'
;
WITH sample AS (
SELECT #start_date AS dt
UNION ALL
SELECT DATEADD(dd, 1, dt)
FROM sample s
WHERE DATEADD(dd, 1, dt) <= #end_date)
, dayofWeekTemp as(
SELECT Cast(DATEPART(dw,s.dt) as int) dayOfWeekValue , s.dt dateValue
FROM sample s)
select Case when dayOfWeekValue =6 then 'End of week' when dayOfWeekValue=2 then 'Start of week' End,dateValue from dayofWeekTemp
where dayOfWeekValue=2 Or dayOfWeekValue=6
Results
End of week 2016-03-04 00:00:00.000
Start of week 2016-03-07 00:00:00.000
End of week 2016-03-11 00:00:00.000
Start of week 2016-03-14 00:00:00.000
End of week 2016-03-18 00:00:00.000
Start of week 2016-03-21 00:00:00.000
End of week 2016-03-25 00:00:00.000
Start of week 2016-03-28 00:00:00.000