Overnight Rules Query - sql

I have data:
Current Data
StartDate
StartTime
EndDate
EndTime
12/10/2020
09:00:00
12/13/2020
12:00:00
And I want an output as follows:
Output Data
StartDate
StartTime
EndDate
EndTime
Duration
12/10/2020
09:00:00
12/10/2020
23:59:59
14:59:59
12/11/2020
00:00:00
12/11/2020
23:59:59
23:59:59
12/12/2020
00:00:00
12/12/2020
23:59:59
23:59:59
12/13/2020
00:00:00
12/13/2020
12:00:00
12:00:00
How is it possible in T-SQL? I am running SQL Server 14.x version.
My attempt:
With cte as(
Select [Start Date] StartDate ,
Case
When [Start Date] = [End Date] Then Duration
When [Start Date] != [End Date] Then DateDiff(MINUTE,[Start Time],'23:59:59')
End
as ShutdownTime
from [dbo].[Delay]
where
[Start Date]>=#StartDate and [Start Date]<= #EndDate
Union
Select [End Date] StartDate,
DateDiff(Minute,'00:00:00', [End Time])
as ShutdownTime
from [dbo].[Delay]
where [Start Date] !=[End Date]
and
[Start Date]>=#StartDate and [Start Date]<= #EndDate
)
Select StartDate, Sum(ShutDownTime/cast(60 as float)) ShutDownTime
from cte group by StartDate
If it is less than 24 hrs. it works fine. but in case of more than 24 hrs, it fails.

First, it is much simpler to work with date/times rather than having them in separate columns.
Second, you want a recursive CTE. Here is working code:
with cte as (
select convert(datetime, startdate) + convert(datetime, starttime) as day_startdt,
(case when t.startdate = t.enddate
then v.enddt else dateadd(day, 1, t.startdate)
end) as day_enddt,
v.enddt, 1 as lev
from t cross apply
(values (convert(datetime, enddate) + convert(datetime, endtime))) v(enddt)
union all
select cte.day_enddt,
(case when cte.day_enddt < convert(date, cte.enddt)
then dateadd(day, 1, cte.day_enddt) else cte.enddt
end) as day_enddt,
cte.enddt, lev + 1
from cte
where cte.day_startdt < dateadd(day, -1, cte.enddt)
)
select *
from cte;
Here is a db<>fiddle.
Note that this includes full days, which makes sense to me. It does not calculate the duration, because that calculation does not look correct in the question -- and if it is, you need to explain it.
You can easily split this into separate date/time columns if you want, although I don't recommend doing that.

I recommend to create a View with calculated column:
CREATE VIEW [schema_name.]view_name
AS SELECT
StartDate, StartTime, EndDate, EndTime,
DATEDIFF(hour, StartTime, EndTime) + ':' +
DATEDIFF(minute, StartTime, EndTime) + ':' +
DATEDIFF(second, StartTime, EndTime) + ':' AS 'Duration'
FROM [schema_name.]table_name;
Links:
CREATE VIEW
DATEDIFF
If your date/time isn't of type DATETIME (or similar), have a look at CAST and CONVERT

DECLARE #StartDate DATE = '2020-10-12';
DECLARE #StartTime TIME = '09:00:00';
DECLARE #EndDate DATE = '2020-12-12';
DECLARE #EndTime TIME = '12:00:00';
WITH cte as (
select
#StartDate as StartDate,
#StartTime as StartTime,
CASE WHEN #StartDate<>#EndDate THEN #StartDate ELSE #EndDate END as EndDate,
CASE WHEN #StartDate<>#EndDate THEN CAST('23:59:59' AS TIME) ELSE #EndTime END as EndTime
union all
select
DATEADD(day,1,StartDate),
StartTime,
DATEADD(day,1,EndDate),
CASE WHEN EndDate<>#EndDate THEN CAST('23:59:59' AS TIME) ELSE #EndTime END as EndTime
from cte
where EndDate <= #EndDate
)
select
StartDate,
StartTime,
EndDate,
EndTime,
CONVERT(TIME,DATEADD(second, DATEDIFF(second,StartTime,EndTime),0)) as Duration
from cte;
The duration is calculated as the difference between StartTime and EndTime, because storing values in a database that depend only on other fields (in the same table) is not a smart thing to do.

Related

Combining Date and Time using DATEADD and DATEDIFF

I'm trying to create a new column by combining the date and time variables. For example, the data table looks like this and "StartDateTime" is the new variable I want to create.
Date StartTime *StartDateTime*
2014-03-20 1900-01-01 10:00:00.000 2014-03-30 10:00:00.000
2015-09-23 1900-01-01 11:00:00.000 2015-09-23 11:00:00.000
I used the cast function and it seems like my current code is working.
select *,
(cast(Date as datetime) + cast(StartTime as datetime)) as StartDateTime
from my_table
But I just saw this line of code on a random website and it seems like it does the same thing. However, I didn't really get the logic behind it.
select *,
DATEADD(day, 0, DATEDIFF(day, 0, Date)) + DATEADD(day, 0 -DATEDIFF(day, 0, StartTime), StartTime) As StartDateTime
from my_table
I believe the first part DATEADD(day, 0, DATEDIFF(day, 0, Date)) just returns the original date but I don't really get the second part. My understanding is DATEDIFF(day, 0, StartTime) would just return 0 and I'm not sure why 0 -DATEDIFF(day, 0, StartTime) is necessary.
Thank you.
This is one way to combine the date and time values into a single DateTime2:
declare #Samples as Table ( StartDate Date, StartTime DateTime2 );
insert into #Samples ( StartDate, StartTime ) values
( '2014-03-20', '1900-01-01 10:00:00.000' ),
( '2015-09-23', '1900-01-01 11:00:00.000' );
select StartDate, StartTime,
-- Demonstrate how to get the time with millisecond resolution from StartTime .
Cast( StartTime as Time(3) ) as StartTimeAsTime,
-- Combine the time from StartTime with the date from StartDate .
-- Get the time, convert it to milliseconds after midnight, and add it to the date as a DateTime2 .
DateAdd( ms, DateDiff( ms, 0, Cast( StartTime as Time(3) ) ), Cast( StartDate as DateTime2 ) ) as StartDateTime
from #Samples;
I have no idea what that code is doing. And the add operator is also not supported by all datetime datatypes. The correct solution is:
select *
, dateadd(second, datepart(second, StartTime), dateadd(minute, datepart(minute, StartTime), dateadd(hour, datepart(hour, StartTime), [Date])))
from (values (convert(datetime2(0),'2014-03-20'), convert(time,'10:00:00.000'))) as X ([Date], StartTime);
Ideally you would store your StartTime value in a time datatype. But the above code will still work with a datetime2 datetype (which is the recommended form of datetime to use).

available room booking between two dates and and two times column sql server

i have four columns in my table.
STARTDATE STARTTIME ENDDATE ENDTIME ROOMCODE
2018-10-16 00:00:00.000 14:00 2018-10-16 00:00:00.000 18:00 CS0001
2018-10-16 00:00:00.000 18:00 2018-10-16 00:00:00.000 22:00 CS0001
i want to check booking is available or not
select CONFERENCE_ROOM from COR_CONFERENCE_BOOKING where
(cast(STARTDATE as datetime) + cast(START_TIME as time) >= cast('2018-10-16'
as datetime) + cast(DATEADD(MINUTE, 01, '14:00') as time)
and cast(STARTDATE as datetime) + cast(START_TIME as time) < cast('2018-10-
16' as datetime) + cast('18:00' as time))
or (cast(ENDDATE as datetime) + cast(END_TIME as time) >= cast('2018-10-16'
as datetime) + cast(DATEADD(MINUTE, 01, '14:00') as time)
and cast(ENDDATE as datetime) + cast(END_TIME as time) < cast('2018-10-16'
as datetime) + cast('18:00' as time))
and CONFERENCE_ROOM='CS0001'
problem is there i want to select the data if any data found on select query on passing date and time then room is booked otherwise its free.
please solve this query. Its too complicated for me.
Here's an example snippet for MS SQL Server.
I'm assuming that this is your target RDBMS because the example SQL uses that DATEADD function.
It'll only return the first record from the table variable.
Because the second record is out of the range.
declare #Table table (
ID INT PRIMARY KEY IDENTITY(1,1),
STARTDATE date,
STARTTIME time,
ENDDATE date,
ENDTIME time,
CONFERENCE_ROOM varchar(6)
);
insert into #Table (STARTDATE, STARTTIME, ENDDATE, ENDTIME, CONFERENCE_ROOM) values
('2018-10-16','14:00:00','2018-10-16','18:00:00','CS0001'),
('2018-10-16','18:00:00','2018-10-16','22:00:00','CS0001');
select ID, CONFERENCE_ROOM
from #Table t
where (CAST(STARTDATE AS DATETIME) + CAST(STARTTIME as DATETIME)) < (CAST(ENDDATE AS DATETIME) + CAST(ENDTIME as DATETIME))
and (CAST(STARTDATE AS DATETIME) + CAST(STARTTIME as DATETIME)) >= CAST('2018-10-16 14:00' AS DATETIME)
and (CAST(ENDDATE AS DATETIME) + CAST(ENDTIME as DATETIME)) <= CAST('2018-10-16 18:00' AS DATETIME)
and CONFERENCE_ROOM = 'CS0001';
If the STARTTIME & ENDTIME are VARCHAR's instead of TIME data types?
Then to avoid errors with trying to convert crap data, you could use TRY_CAST instead.
(If TRY_CAST is available on your version of MS SQL Server)
Because instead of an error a TRY_CAST would return NULL when the conversion fails.
Example:
select ID, CONFERENCE_ROOM
from #Table t
where STARTDATE = ENDDATE
and TRY_CAST(STARTTIME AS TIME) < TRY_CAST(ENDTIME AS TIME)
and STARTDATE = CAST('2018-10-16' AS DATE)
and TRY_CAST(STARTTIME AS TIME) >= CAST('14:00' AS TIME)
and TRY_CAST(ENDTIME AS TIME) <= CAST('18:00' AS TIME)
and CONFERENCE_ROOM = 'CS0001';

Split hours between dates based on date range

Split hours spanning between multiple days
I have data like this.
StartDate EndDate
2015-10-05 20:00:00.000 2015-10-06 12:00:00.000
I need this data to be split by date like
2015-10-05 240 (4 hours)
2015-10-06 720 (12 hours)
I can get the first start date split like this
select (StartDate as date) as StartDate,
DATEDIFF(minute, StartDate, dateadd(day, 1, Cast(StartDate as date))) as diff
from t
which gives me
2015-10-05 240
And get the end date's data like
select
Cast(EndDate as date) as StartDate,
DATEDIFF(minute, Cast(EndDate as date), EndDate) as diff
from t
2015-10-06 720
But I am not sure how to do it all in one query. Besides, the time diff betweens start and end can be more than one day diff like this
StartDate EndDate
2015-10-05 20:00:00.000 2015-10-08 12:00:00.000
for which I need
2015-10-05 240
2015-10-06 1440
2015-10-07 1440
2015-10-06 720
Thanks for looking
This should cover when start and end are on the same date and no limit on days
EDIT: fixed issue with incorrect calculation for the end date
declare #StartDate datetime, #EndDate datetime
set #StartDate = '2015-10-05 20:00'
set #EndDate = '2015-10-05 21:00'
;WITH cte AS
(
SELECT cast(#StartDAte as date) StartDate,
cast(dateadd(day, 1, #StartDAte) as date) EndDate
UNION ALL
SELECT DATEADD(day, 1, StartDate) StartDate,
DATEADD(day, 2, StartDate) EndOfDate
FROM cte
WHERE DATEADD(day, 1, StartDate) <= #EndDate
)
select StartDate,
case
when cast(#StartDate as date) = cast(#EndDate as date) then datediff(minute, #StartDate, #EndDate )
when StartDate = cast(#StartDate as date) then datediff(minute, #StartDate, cast(EndDate as datetime))
when StartDate = cast(#EndDate as date) then datediff(minute, cast(StartDate as datetime), #EndDate)
else 1440 end
from cte
Try it like this:
Remark: If the full intervall can be more than 10 days just add some more values to the tally table. Attention: This solution does not yet cover the situation, when start and end is on the same day...
DECLARE #d1 DATETIME={ts'2015-10-05 08:00:00'};
DECLARE #d2 DATETIME={ts'2015-10-07 12:00:00'};
WITH SomePreCalculations AS
(
SELECT #d1 AS D1
,#d2 AS D2
,CAST(#d1 AS DATE) AS StartDate
,DATEADD(DAY,1,CAST(#d1 AS DATE)) AS FirstMidnight
,CAST(#d2 AS DATE) AS LastMidnight
)
,Differences AS
(
SELECT *
,DATEDIFF(MINUTE,D1,FirstMidnight) AS TilMidnight
,DATEDIFF(MINUTE,LastMidnight,D2) AS FromMidnight
FROM SomePreCalculations
)
,TallyTable AS
(
SELECT RowInx FROM (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) AS x(RowInx)
)
SELECT CAST(Date AS DATE),Minutes FROM
(
SELECT 0 AS Inx, D1 AS Date, TilMidnight AS Minutes
FROM Differences
UNION SELECT RowInx,(SELECT DATEADD(DAY,RowInx,(SELECT StartDate FROM SomePreCalculations))),1440
FROM TallyTable
WHERE DATEADD(DAY,RowInx,(SELECT StartDate FROM SomePreCalculations))<(SELECT LastMidnight FROM SomePreCalculations)
UNION SELECT 99 AS Inx, D2, FromMidnight
FROM Differences
) AS tbl
ORDER BY tbl.Inx

How to differentiate two time timeperiod between two columns?

I wanna know how to differentiate a time period in a day between two columns like in time and out time?
DATEDIFF
DATEDIFF (datepart , startdate , enddate)
The datepart you have mentioend in your question is day. You can use any of day, d and dd
Sample
DECLARE #inTime DATETIME, #outTime DATETIME
SET #inTime = '2015-05-01 12:10:09'
SET #outTime = '2015-05-22 05:15:36'
SELECT DATEDIFF(DAY, #inTime, #outTime) AS timedifferentiate
In a query
SELECT ,inTime
,outTime
DATEDIFF(DAY, inTime, outTime) AS timedifferentiate
FROM yourTable
Just use a minus operator, and convert to time
SELECT
CONVERT(time, -- Note the time datatype range is 00:00:00.0000000 through 23:59:59.9999999
CONVERT(datetime, '2015-04-01 19:03') -- Out time
- CONVERT(datetime, '2015-04-01 09:02') -- In time
) TimeDiff

Get start and end dates of month in between two dates in SQL?

I have the following function which is supposed to return the start and end dates of the months in between two months, the problem how ever is that since this month is 28 days the function is calculating all the upcoming months on a 28 day basis thus returning the following wrong values.
StartDate EndDate
-----------------------
2013-02-01 2013-02-28
2013-03-01 2013-03-28
2013-04-01 2013-04-28
declare #sDate datetime,
#eDate datetime;
select #sDate = '2013-02-25',
#eDate = '2013-04-25';
;with months as
(
select DATEADD(mm,DATEDIFF(mm,0,#sDate),0) StartDate,
DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,#sDate)+1,0)) EndDate
union all
select dateadd(mm, 1, StartDate),
dateadd(mm, 1, EndDate)
from months
where dateadd(mm, 1, StartDate)<= #eDate
)
select * from months
how can I modify this to return the right dates?
Try this;
declare #sDate datetime,
#eDate datetime
select #sDate = '2013-02-25',
#eDate = '2013-04-25'
;with cte as (
select convert(date,left(convert(varchar,#sdate,112),6) + '01') startDate,
month(#sdate) n
union all
select dateadd(month,n,convert(date,convert(varchar,year(#sdate)) + '0101')) startDate,
(n+1) n
from cte
where n < month(#sdate) + datediff(month,#sdate,#edate)
)
select startdate, dateadd(day,-1,dateadd(month,1,startdate)) enddate
from cte
FIDDLE DEMO
| STARTDATE | ENDDATE |
---------------------------
| 2013-02-01 | 2013-02-28 |
| 2013-03-01 | 2013-03-31 |
| 2013-04-01 | 2013-04-30 |
If you can get the first day of the month, use dateadd twice to get the last day.
First, add 1 month, then subtract 1 day.
Try This : It's a table function. Return a table of month start and end Date with partial Date Also.
CREATE FUNCTION [dbo].[Fun_GetFirstAndLastDateOfeachMonth] (
#StartDate DATE,
#EndDate DATE
)
RETURNS #Items TABLE (
StartDate DATE ,EndDate DATE,MonthNumber INT,YearNumber INT
)
AS
BEGIN
;WITH cte AS (
SELECT CONVERT(DATE,LEFT(CONVERT(VARCHAR,#StartDate,112),6) + '01') startDate,
MONTH(#StartDate) n
UNION ALL
SELECT DATEADD(MONTH,N,CONVERT(DATE,CONVERT(VARCHAR,YEAR(#StartDate)) + '0101')) startDate,
(n+1) n
FROM cte
WHERE n < MONTH(#StartDate) + DATEDIFF(MONTH,#StartDate,#EndDate)
)
INSERT INTO #Items
SELECT CASE WHEN MONTH(startdate) = MONTH(#StartDate) THEN #StartDate ELSE startdate END AS StartDate,
CASE WHEN MONTH(startdate) = MONTH(#EndDate) THEN #EndDate ELSE DATEADD(DAY,-1,DATEADD(MONTH,1,startdate)) END AS enddate,
MONTH(startDate) AS MonthNumner,YEAR(startDate) AS YearNumber
FROM cte
RETURN
END -- End Function
Execute This After Creating The Function
SELECT * From [EDDSDBO].[Fun_GetFirstAndLastDateOfeachMonth] ('2019-02-25','2019-05-20')
You can use the code below
create FUNCTION [dbo].fn_getfirstandenddate (
#StartDate DATE,
#EndDate DATE
)
RETURNS #Items TABLE (
StartDate DATE ,EndDate DATE
)AS
BEGIN
insert into #ItemsSELECT case when #startdate > DATEADD(DAY,1,EOMONTH((DATEADD(MONTH, x.number, #StartDate)),-1)) then #startdate else DATEADD(DAY,1,EOMONTH((DATEADD(MONTH, x.number, #StartDate)),-1)) end startDay,case when #enddate < eomonth(DATEADD(MONTH, x.number, #StartDate)) then #enddate else eomonth(DATEADD(MONTH, x.number, #StartDate)) end endDate FROM master.dbo.spt_values x WHERE x.type = 'P'AND x.number <= DATEDIFF(MONTH, #StartDate, #EndDate);
RETURN
END
End of Month:
#pDate = EOMONTH(GETDATE())
Starting Date:
#pDate = DATEADD(DAY,-1 * (DAY(GETDATE())-1),GETDATE())