I have a Start Datetime and a End Datetime.
Eg: 10:00 am - 12:00 pm
I have to create 4 slots between the, each ranging for 30 minutes.
Eg:
10:00 am-10:30 am
10:30 am-11:00 am
11:00 am-11:30 am
11:30 am-12:00 pm
This 30 minutes can vary and is not a constant. I have tried a few things but they don't seem to work. Can someone please help. Thank you.
I have tried this but i only get the slot difference not the slotfrom-slotto
SELECT
from_dt,to_dt,
DATEDIFF(mi,DATEADD(dd,DATEDIFF(dd,0,from_dt ),0),to_dt )/60 as SlotNumber
FROM
d
group by from_dt,to_dt, DATEDIFF(mi,DATEADD(dd,DATEDIFF(dd,0,from_dt ),0),to_dt )/60
thanks #dwain.c to get Reference
--Declare table
DECLARE #t TABLE
(StartTime TIME, EndTime TIME)
INSERT INTO #t
SELECT '10:00', '12:00'
-- Make CTE
;WITH CTE (n) AS (
SELECT TOP (SELECT DATEDIFF(MINUTE,StartTime,EndTime)/30
FROM #t) 30*(ROW_NUMBER() OVER (ORDER BY (SELECT NULL))-1)
FROM sys.all_columns
)
-- QUERY
SELECT
TSStart=DATEADD(minute, n, StartTime)
,TSEnd=DATEADD(minute, n + 30, StartTime)
,Timeslot=CONVERT(VARCHAR(100), DATEADD(minute, n, StartTime), 0) + ' - ' +
CONVERT(VARCHAR(100), DATEADD(minute, n + 30, StartTime), 0)
FROM #t
CROSS APPLY (
SELECT n
FROM CTE
WHERE n BETWEEN 0 AND DATEDIFF(minute, StartTime, DATEADD(minute, -30, EndTime))) a
ORDER BY TSStart
Related
Starting data:
Desired results something like this:
So it calculated the number of hours until the end of StartDateTime, if the EndDateTime is greater than end of day for StartDateTime. Then for every full day in between, it calculates 24 hours (this could stretch numerous days). And then when it gets to the EndDateTime - it calculates time from midnight (morning) to EndDateTime
I'm reading that I will probably need to use a recursive CTE, but I don't have any experience with recursions and am struggling.
this might get tricky, but I guess it can be solved using so called number table - i.e. table which has only one column populated with number sequence. In our case 0 based sequence.
The trick here is to get the number of days between start and end datetime. This value used in join between the data table and the numbers table will create the needed extra rows for each per day interval.
Of course we also have to setup properly start and end datetime of each day interval (CASE terms in the CTE)
Then we get for each per day interval number of minutes and divide by 60 to get proper decimal value.
Hope this helps.
Lets see the code:
-- input data
DECLARE #v_Dates TABLE
(
id varchar(20),
StartDateTime SMALLDATETIME,
EndDateTime SMALLDATETIME
)
INSERT INTO #v_Dates (id, StartDateTime, EndDateTime)
VALUES ('example 1', '02-17-2019 0:45', '02-19-19 12:30'),
('example 2', '02-21-2019 18:00', '02-22-19 12:15'),
('example 3', '02-22-2019 20:15', '02-22-19 20:30');
-- so called Number table which holds numbers 0 - 9999 in this case
DECLARE #v_Numbers TABLE
(
Number INT
);
-- populating the number table
INSERT INTO #v_Numbers
SELECT TOP 10000 ROW_NUMBER() OVER(ORDER by t1.number) - 1 as Number
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
-- we parse the dates into the per day intervals
;WITH IntervalsParsed(id, StartDateTime, EndDateTime, Number, IntervalStartDateTime, IntervalEndDateTime) AS
(
SELECT id
,StartDateTime
,EndDateTime
,Number
, InervalStartDateTime = CASE
WHEN D.StartDateTime > DATEADD(day, DATEDIFF(day, 0, D.StartDateTime), N.Number) THEN D.StartDateTime
ELSE DATEADD(day, DATEDIFF(day, 0, D.StartDateTime), N.Number)
END
, IntervalEndDateTime = CASE
WHEN D.EndDateTime < DATEADD(day, DATEDIFF(day, 0, D.StartDateTime), N.Number + 1) THEN D.EndDateTime
ELSE DATEADD(day, DATEDIFF(day, 0, D.StartDateTime), N.Number + 1)
END
FROM #v_Dates D
--this join basically creates the needed number of rows
INNER JOIN #v_Numbers N ON DATEDIFF(day, D.StartDateTime, D.EndDateTime) + 1 > N.Number
)
-- final select
SELECT id
, StartDateTime
, EndDateTime
, IntervalStartDateTime
, IntervalEndDateTime
, Number
, DecimalValue = CAST( DATEDIFF(minute, IntervalStartDateTime, IntervalEndDateTime) AS DECIMAL)/60
FROM IntervalsParsed
ORDER BY id, Number
Just another option is an ad-hoc tally table in concert with a CROSS APPLY
Example
Select A.[column1]
,A.[StartDateTime]
,A.[EndDateTime]
,Hours = sum(1) / 60.0
From #YourTable A
Cross Apply (
Select Top (DateDiff(MINUTE,[StartDateTime],[EndDateTime])+1)
D=DateAdd(MINUTE,-1+Row_Number() Over (Order By (Select Null)),[StartDateTime])
From master..spt_values n1,master..spt_values n2
) B
Group By [column1],[StartDateTime],[EndDateTime],cast(D as Date)
Returns
This may be little complicated, but here is one way to use recursive cte to get the output. You can add the start date with one day as long as it is less than end date of your column. Also declared a Static value to make sure we can get difference of 24 hours.
--Create a table
Select 'example1' exm, '2019-02-17 00:45:00' startdate, '2019-02-19 12:30:00' Enddate into #temp union all
Select 'example2' exm, '2019-02-21 18:00:00' startdate, '2019-02-22 12:15:00' Enddate union all
Select 'example3' exm, '2019-02-22 20:15:00' startdate, '2019-02-22 20:30:00' Enddate
Declare #datevalue time = '23:59:59'
;with cte as (select exm, startdate, enddate, case when datediff(day, startdate, enddate) = 0 then datediff(SECOND, startdate, enddate)
when datediff(day, startdate, enddate)>0 then
datediff(SECOND, cast(startdate as time), #datevalue)
end as Hoursn, cast(dateadd(day, 1,cast(startdate as date)) as smalldatetime) valueforhours from #temp
union all
select exm, startdate, enddate, case when datediff(day, valueforhours, enddate) = 0 then datediff(SECOND, valueforhours, enddate)
when datediff(day, valueforhours, enddate)>0 then datediff(SECOND, cast(valueforhours as time), #datevalue) end as Hoursn, case when datediff(day,valueforhours, enddate) > 0 then dateadd(day,1,valueforhours) end as valueforhours
from cte
where
valueforhours <= cast(enddate as date)
)
select exm, startdate, Enddate, round(Hoursn*1.0/3600,2) as [hours] from cte
order by exm
Output:
exm startdate Enddate hours
example1 2019-02-17 00:45:00 2019-02-19 12:30:00 23.250000
example1 2019-02-17 00:45:00 2019-02-19 12:30:00 24.000000
example1 2019-02-17 00:45:00 2019-02-19 12:30:00 12.500000
example2 2019-02-21 18:00:00 2019-02-22 12:15:00 6.000000
example2 2019-02-21 18:00:00 2019-02-22 12:15:00 12.250000
example3 2019-02-22 20:15:00 2019-02-22 20:30:00 0.250000
In SQL SERVER How do you group by any time based interval?
To save someone time I have come up with this solution, For me it works very well. You can generate any time base then group by any interval. Great for doing time weighted averages. If someone has a better way of doing this I would love to hear from you.
Hours
declare #startdate datetime2
declare #enddate datetime2
declare #interval int
set #startdate = '2017-01-01 00:00:00'
set #enddate = '2017-01-31 00:00:00'
set #interval = 4 --Group by Every 4 hours
;with
ALL_INTERVALS
AS (
SELECT TOP (DATEDIFF(HOUR,#startdate,#enddate))
TIMES = DATEADD(HOUR,CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[object_id])),#startdate),
1 AS VALUE
FROM sys.all_objects AS s1
CROSS JOIN
sys.all_objects AS s2
)
select DATEADD(HOUR,((DATEDIFF(HOUR, #startdate,TIMES)/#interval)*#interval),#startdate) AS TIMES,SUM(VALUE) AS TESTDATA
from ALL_INTERVALS
group by DATEADD(HOUR,((DATEDIFF(HOUR, #startdate,TIMES)/#interval)*#interval),#startdate)
order by DATEADD(HOUR,((DATEDIFF(HOUR, #startdate,TIMES)/#interval)*#interval),#startdate)
Minutes
Note.
you can set your interval to 60 to achieve hours, 1440 to achieve days....
declare #startdate datetime2
declare #enddate datetime2
declare #interval int
set #startdate = '2017-01-01 00:00:00'
set #enddate = '2017-01-31 00:00:00'
set #interval = 7
;with
ALL_INTERVALS
AS (
SELECT TOP (DATEDIFF(MINUTE,#startdate,#enddate))
TIMES = DATEADD(MINUTE,CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[object_id])),#startdate),
1 AS VALUE
FROM sys.all_objects AS s1
CROSS JOIN
sys.all_objects AS s2
)
select DATEADD(MINUTE,((DATEDIFF(MINUTE, #startdate,TIMES)/#interval)*#interval),#startdate) AS TIMES,SUM(VALUE) AS TESTDATA
from ALL_INTERVALS
group by DATEADD(MINUTE,((DATEDIFF(MINUTE, #startdate,TIMES)/#interval)*#interval),#startdate)
order by DATEADD(MINUTE,((DATEDIFF(MINUTE, #startdate,TIMES)/#interval)*#interval),#startdate)
I think you are over complicating thins.
You can use GROUP BY (DATEDIFF(MINUTE, '2017-01-01', TheDateTime) / 30 for grouping by every 30 minutes. Of course, the date I've chosen is a just a random date. You can choose, if you want, the first (or last) date in your sample data.
And you can also use this technique to get every interval of any time part - just change the keyword MINUTE to any date part you want to use, and the intreval 30 to any interval you want.
Consider the following sample data:
;WITH CTE AS
(
SELECT CAST('2017-01-01T00:00:00' as datetime) As TheDateTime, 0 as rn
UNION ALL
SELECT DATEADD(MINUTE, 1, TheDateTime), rn + 1
FROM CTE
WHERE rn < 60
)
SELECT TheDateTime, rn INTO #T
FROM CTE
OPTION(MAXRECURSION 0)
#T now contains the following data:
TheDateTime rn
2017-01-01 00:00:00.000 0
2017-01-01 00:01:00.000 1
2017-01-01 00:02:00.000 2
2017-01-01 00:03:00.000 3
...
2017-01-01 00:59:00.000 59
2017-01-01 01:00:00.000 60
To get the maximum rn grouped by 30 minutes you just need this:
SELECT DATEDIFF(MINUTE, '2017-01-01', TheDateTime) / 30, MAX(rn)
FROM #T
GROUP BY DATEDIFF(MINUTE, '2017-01-01', TheDateTime) / 30
Results:
interval max_rn
0 29
1 59
2 60
I need to get last day of all previous months including current month, upto a specified month. For example, I need last days of september, aug, july, june, may, april, march, feb, jan, dec 2015 like so:
temptable_mytable:
last_day_of_month
-----------------
2016-09-30
2016-08-31
2016-07-31
2016-06-30
2016-05-31
2016-04-30
2016-03-31
2016-02-30
2016-01-31
2015-12-31
I need to specify the month and year to go back to - in above case it's December 2015, but it could also be September 2015 and such. Is there a way that I can do a loop and do this instead of having to calculate separately for each month end?
Use a recursive CTE with the EOMONTH function.
DECLARE #startdate DATE = '2016-01-01'
;WITH CTE
AS
(
SELECT EOMONTH(GETDATE()) as 'Dates'
UNION ALL
SELECT EOMONTH(DATEADD(MONTH, -1, [Dates]))
FROM CTE WHERE Dates > DATEADD(MONTH, 1, #startdate)
)
SELECT * FROM CTE
with temp as (select -1 i union all
select i+1 i from temp where i < 8)
select DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+i*-1,0)) from temp
declare #LASTMONTH date = '2018-10-01';
WITH MTHS AS (
SELECT dateadd(month,month(getdate()),dateadd(year,year(getdate()) - 1900, 0)) aday
UNION ALL
SELECT DATEADD(month,1,aday) from MTHS WHERE aday <= #LASTMONTH
),
LASTDAYS AS (SELECT DATEADD(day,-1,aday) finaldayofmonth from MTHS)
select * from LASTDAYS
Here is a version that goes forward or backwards as appropriate
declare #LASTMONTH date = '2013-10-01';
WITH DIF AS (SELECT CASE WHEN
YEAR(#LASTMONTH) * 12 + MONTH(#LASTMONTH)
>= YEAR(GETDATE()) * 12 + MONTH(getdate()) THEN 1 ELSE -1 END x),
MTHS AS (
SELECT dateadd(month,month(getdate()),dateadd(year,year(getdate()) - 1900, 0)) aday
UNION ALL
SELECT DATEADD(month,(SELECT X from dif),aday) from MTHS
WHERE month(aday) != month(dateadd(month,1,#LASTMONTH)) or YEAR(aday) != YEAR(dateadd(month,1,#LASTMONTH))
),
LASTDAYS AS (SELECT DATEADD(day,-1,aday) finaldayofmonth from MTHS)
select * from LASTDAYS order by finaldayofmonth
Here's one approach, using a CTE to generate a list of incrementing numbers to allow us to then have something to select from and use in a DATEADD to go back for the appropriate number of months.
Typically, if you're doing this quite frequently, instead of generating numbers on the fly like this with the CROSS JOIN, I'd recommend just creating a "Numbers" table that just holds numbers from 1 to "some number high enough to meet your needs"
DECLARE #Date DATE = '20151201'
DECLARE #MonthsBackToGo INTEGER
SELECT #MonthsBackToGo = DATEDIFF(mm, #Date, GETDATE()) + 1;
WITH _Numbers AS
(
SELECT TOP (#MonthsBackToGo) ROW_NUMBER() OVER (ORDER BY o.object_id) AS Number
FROM sys.objects o
CROSS JOIN sys.objects o2
)
SELECT EOMONTH(DATEADD(mm, -(Number- 1), GETDATE())) AS last_day_of_month
FROM _Numbers
This should scale out no matter how far you go back or forward for your originating table or object.
SET NOCOUNT ON;
DECLARE #Dates TABLE ( dt DATE)
DECLARE #Start DATE = DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0)
DECLARE #End DATE = DATEADD(YEAR, 1, #Start)
WHILE #Start <= #End
BEGIN
INSERT INTO #Dates (dt) VALUES (#Start)
SELECT #Start = DATEADD(DAY, 1, #Start)
END
; With x as
(
Select
dt
, ROW_NUMBER() OVER(PARTITION BY DATEPART(YEAR, Dt), DATEPART(MONTH, Dt) ORDER BY Dt Desc) AS rwn
From #Dates
)
Select *
From x
WHERE rwn = 1
ORDER BY Dt
This was cribbed together quick based on a couple different SO answers for the parts:
DECLARE #startdate datetime, #enddate datetime
set #startdate = '2015-12-01'
set #enddate = getdate()
;WITH T(date)
AS
(
SELECT #startdate
UNION ALL
SELECT DateAdd(day,1,T.date) FROM T WHERE T.date < #enddate
)
SELECT DISTINCT
DATEADD(
day,
-1,
CAST(CAST(YEAR(date) AS varchar) + '-' + CAST(MONTH(date)AS varchar) + '-01' AS DATETIME))
FROM T OPTION (MAXRECURSION 32767);
How do I select 6/20/2014 and every pay date (every two weeks) before this date to the beginning of 2010? I also want to be able to select 6/20/2014 and every pay date (every 2 weeks) after this date to the end of 2020.
Thank you!
The SQL below can give you the answer to the first part of your question. Same principals can be used to get the answer to the second part.
declare #startDate as date = '20140620';
with myCte1 as
(
select #startDate as dt
union all
select DATEADD(week, -2, dt)
from myCte1
where dt > '20100112'
)
select m.dt
from myCte1 as m
option (maxrecursion 1000)
Create a Numbers table:
SELECT TOP 10000 IDENTITY(int,1,1) AS Number
INTO NumbersTest
FROM sys.objects s1
CROSS JOIN sys.objects s2
ALTER TABLE NumbersTest ADD CONSTRAINT PK_NumbersTest PRIMARY KEY CLUSTERED (Number)
Then
declare #startDate date = '6/20/2014'
--Dates from 1/1/2010 to 6/20/2014
select DATEADD(wk, -2 * (n.Number - 1), #startDate)
from NumbersTest n
where DATEADD(wk, -2 * (n.Number - 1), #startDate) >= '1/1/2010'
--Dates from 6/20/2014 to 12/31/2020
select DATEADD(wk, 2 * (n.Number - 1), #startDate)
from NumbersTest n
where DATEADD(wk, 2 * (n.Number - 1), #startDate) <= '12/31/2020'
SQL Fiddle
I want to write sql query which will give out put as below
Query:
DivideTime('10','10:00:00','11:00:00')
OUTPUT:
10:10
10:20
10:30
10:40
10:50
11:00
This could be done with a recursive common table expressions
CREATE FUNCTION [dbo].[get_time_range] (#interval INT, #start_time TIME, #end_time TIME)
RETURNS #tbl TABLE ([time] TIME)
AS
BEGIN
WITH [cte_recursion] AS
(
SELECT DATEADD(MINUTE, #interval, #start_time) [time]
UNION ALL
SELECT
DATEADD(MINUTE, #interval, [time]) [time]
FROM [cte_recursion]
WHERE [time] < #end_time
)
INSERT INTO #tbl ([time])
SELECT
[time]
FROM [cte_recursion]
OPTION (MAXRECURSION 0); -- allows more than 100 iterations
RETURN;
END
GO
SELECT
*
FROM [dbo].[get_time_range](10, '10:00:00', '11:00:00');
Using the numbers table:
select dateadd(minute, 10*n, #start_time)
from numbers n
where n <= datediff(minute, #start_time, #end_time)/#interval
http://dataeducation.com/you-require-a-numbers-table/