Calculating the business differences in sql in the form of day, hours and minutes - SQL Server - date

With the below function I getting the result as 00:09:10 however I want the result to be as 01:00:10.
So hours is considered as considered as 1 day.
Example if hours is 30 then it will be 03:03:00 and so on
fn_GetHolidayMinutes : Get holiday in minutes between two dates and country
CREATE FUNCTION [dbo].[fn_GetHolidayMinutes]
(#StartDate DATETIME,
#EndDate DATETIME,
#CountryId BIGINT)
RETURNS BIGINT
AS
BEGIN
DECLARE #OUTPUT BIGINT;
DECLARE #HolidayList TABLE (HolidaysDate DATE)
-- Create Table #HolidayList
-- (
-- HolidaysDate date
-- )
DECLARE #Date1 DATE, #Date2 DATE
DECLARE holiday_cursor CURSOR FOR
SELECT StartDate,EndDate
FROM Holidays
WHERE IsActive = 1
AND CountryId = #CountryId
AND ((StartDate BETWEEN #StartDate AND #EndDate) OR
(EndDate BETWEEN #StartDate AND #EndDate))
OPEN HOLIDAY_CURSOR
FETCH NEXT FROM HOLIDAY_CURSOR INTO #Date1, #Date2
WHILE ##FETCH_STATUS = 0
BEGIN
--INSERT INTO #HolidayList
INSERT INTO #HolidayList
SELECT DATEADD(DAY, number, #Date1) [Date]
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY, number, #Date1) <= #Date2
FETCH NEXT FROM HOLIDAY_CURSOR INTO #Date1, #Date2
END
CLOSE HOLIDAY_CURSOR;
DEALLOCATE HOLIDAY_CURSOR;
(SELECT #OUTPUT= COUNT(DISTINCT HolidaysDate)
FROM #HolidayList
WHERE HolidaysDate BETWEEN #StartDate AND #EndDate
AND DATEPART(dw, HolidaysDate) NOT IN (SELECT DISTINCT number
FROM master..spt_values
WHERE number BETWEEN 1 and 7
AND number NOT IN (SELECT WorkingDay
FROM WorkingDays
WHERE CountryId = #CountryId AND IsActive = 1)
))
---print #OUTPUT; --this will give in days
--get the output in minutes
RETURN #OUTPUT * (SELECT TOP 1 STUFF(WorkingHours, 2, 2, '')
FROM dbo.WorkingDays
WHERE CountryId = #CountryId) * 60;
END
fn_GetWorkingDayMinuts :
CREATE FUNCTION [dbo].[fn_GetWorkingDayMinuts]
(#StartDate DATETIME,
#EndDate DATETIME,
#CountryId BIGINT)
--RETURNS BIGINT
RETURNS VARCHAR(250)
AS
BEGIN
DECLARE #Temp BIGINT
SET #Temp = 0
DECLARE #FirstDay DATE
SET #FirstDay = CONVERT(DATE, #StartDate, 112)
DECLARE #LastDay DATE
SET #LastDay = CONVERT(DATE, #EndDate, 112)
DECLARE #StartTime TIME
SET #StartTime = CONVERT(TIME, #StartDate)
DECLARE #FinishTime TIME
SET #FinishTime = CONVERT(TIME, #EndDate)
DECLARE #WorkStart TIME
SET #WorkStart = (SELECT CONVERT(VARCHAR(5),CONVERT(TIME, CONVERT(VARCHAR,CONVERT(DATE, GETDATE()))+ ' ' + (SELECT TOP 1
WorkStartTime FROM WorkingDays WHERE CountryId=#CountryId), 120)))
DECLARE #WorkFinish TIME
SET #WorkFinish = (SELECT CONVERT(VARCHAR(5),CONVERT(TIME, CONVERT(VARCHAR,CONVERT(DATE, GETDATE()))+ ' ' + (SELECT TOP 1
WorkEndTime FROM WorkingDays WHERE CountryId=#CountryId), 120)))
DECLARE #DailyWorkTime BIGINT
SET #DailyWorkTime = DATEDIFF(MINUTE, #WorkStart, #WorkFinish)
IF (#StartTime<#WorkStart)
BEGIN
SET #StartTime = #WorkStart
END
IF (#FinishTime>#WorkFinish)
BEGIN
SET #FinishTime=#WorkFinish
END
IF (#FinishTime<#WorkStart)
BEGIN
SET #FinishTime=#WorkStart
END
IF (#StartTime>#WorkFinish)
BEGIN
SET #StartTime = #WorkFinish
END
DECLARE #CurrentDate DATE
SET #CurrentDate = #FirstDay
DECLARE #LastDate DATE
SET #LastDate = #LastDay
WHILE(#CurrentDate<=#LastDate)
BEGIN
--IF (DATEPART(dw, #CurrentDate)!=1 AND DATEPART(dw, #CurrentDate)!=7)
IF(DATEPART(dw, #CurrentDate) IN (SELECT distinct number FROM master..spt_values WHERE number BETWEEN 1 and 7
AND number NOT IN (SELECT
WorkingDay FROM WorkingDays where CountryId=#CountryId and IsActive=1)
))
BEGIN
IF (#CurrentDate!=#FirstDay) AND (#CurrentDate!=#LastDay)
BEGIN
SET #Temp = #Temp + #DailyWorkTime
END
--IF it starts at startdate and it finishes not this date find diff between work finish and start as minutes
ELSE IF (#CurrentDate=#FirstDay) AND (#CurrentDate!=#LastDay)
BEGIN
SET #Temp = #Temp + DATEDIFF(MINUTE, #StartTime, #WorkFinish)
END
ELSE IF (#CurrentDate!=#FirstDay) AND (#CurrentDate=#LastDay)
BEGIN
SET #Temp = #Temp + DATEDIFF(MINUTE, #WorkStart, #FinishTime)
END
--IF it starts and finishes in the same date
ELSE IF (#CurrentDate=#FirstDay) AND (#CurrentDate=#LastDay)
BEGIN
SET #Temp = DATEDIFF(MINUTE, #StartTime, #FinishTime)
END
END
SET #CurrentDate = DATEADD(day, 1, #CurrentDate)
END
-- Return the result of the function
IF #Temp<0
BEGIN
SET #Temp=0
END
--RETURN #Temp -(dbo.fn_GetHolidayMinutes (DATEADD(dd, 0, DATEDIFF(dd, 0, #StartDate)),DATEADD(dd, 0, DATEDIFF(dd, 0,
#EndDate)),#CountryId))
--RETURN #Temp
DECLARE #theMinutes INT
DECLARE #Result VARCHAR(250)
SET #theMinutes = #Temp -(dbo.fn_GetHolidayMinutes (DATEADD(dd, 0,
DATEDIFF(dd, 0, #StartDate)),DATEADD(dd, 0, DATEDIFF(dd, 0,
#EndDate)),#CountryId))
--SET #Result= concat((#theMinutes / 540),':' , (#theMinutes % 540) /
60, ':', (#theMinutes % 60))
SET #Result= concat((#theMinutes / ((SELECT TOP 1
STUFF(WorkingHours,2,2,'') FROM dbo.WorkingDays WHERE
CountryId=#CountryId) * 60)),':' , (#theMinutes % ((SELECT TOP 1
STUFF(WorkingHours,2,2,'') FROM dbo.WorkingDays WHERE
CountryId=#CountryId) * 60)) / 60, ':', (#theMinutes % 60))
RETURN #Result
END
So where to modify to the result as
BUSINESS HOURS CREATE DATE & TIME FIRST APPLY (DATE & TIME) TAT TIME CALCULATION RESULT
08h00-17h00: 9hrs/day 12-FEB-19 14:20 13-FEB-19 14:30 00:02:40 + 00:06:30 = 00:09:10 01:00:10

Related

Executing a trigger with some linked server components

I have a row being inserted into [Server1].[DatabaseA].[dbo].[EMRVisit]. I need to add some information to the Visit record from linked server [Server2].[DatabaseB], so I set up an AFTER INSERT trigger.
Both servers are MSSQL 2016, databases are running at 2008 compatibility at the moment.
Currently, the trigger is blocking insertion of the row. I'm not seeing anything in Profiler, other that the Insert attempt and a rollback. Also, it's not generating any other errors. The code runs fine outside the trigger.
The frustrating part of this, is that the trigger was operating fine as is on older 2008 boxes.
ALTER TRIGGER [dbo].[Pt_Note_Edit]
ON [dbo].[EMRVisit]
AFTER INSERT
AS
IF ((SELECT TRIGGER_NESTLEVEL()) < 2)
BEGIN
DECLARE #med_rec_nbr varchar(15), #Days VARCHAR(20), #OldNote nvarchar(MAX), #NewNote nvarchar(MAX)
SET #med_rec_nbr = (SELECT ept.PatientChartNumber FROM INSERTED ev INNER JOIN [Server1].[DatabaseA].[dbo].[Patients] ept ON ev.PtID = ept.PtID)
--Collect ADS Data
--Convert #med_rec_nbr to #Account
DECLARE #Account varchar(15)
EXEC [Server2].[DatabaseB].[dbo].ADS_CleanUp_MRN #med_rec_nbr, #Account=#Account OUTPUT
SELECT *
INTO #PtChgs
FROM [Server2].[DatabaseB].[dbo].ADS_Charges
WHERE AccountNo = #Account
--Get Old Pt Note
SELECT #OldNote = ept.PatientNote
FROM [Server1].[DatabaseA].[dbo].[Patients] ept
WHERE ept.PatientChartNumber = #med_rec_nbr
--Edit OldNote
DECLARE #NotePart nvarchar(MAX), #Trans Int
SET #NotePart = CASE
WHEN #OldNote IS NOT NULL THEN SUBSTRING(#OldNote,CHARINDEX('~',#OldNote,1)+1,LEN(#OldNote))
ELSE ''
END
--Get Latest Post Op Charges
SELECT Charges.FromDate
,Charges.Modifiers
,DATEDIFF(DAY, Charges.FromDate, GETDATE()) AS DaysIn
INTO #PtPostOp
FROM [Server2].[DatabaseB].[dbo].[Charges] Charges
JOIN [Server2].[DatabaseB].[dbo].[ProcedureCodes] PC ON Charges.Cpt = PC.Code
WHERE Charges.AccountNo = #Account
AND Charges.FromDate > CONVERT(VARCHAR, DATEADD (DAY , -(PC.NumberOfDays + 10) , GETDATE()), 112)
AND PC.NumberOfDays <> 0
ORDER BY Charges.FromDate DESC
DECLARE #FromDate integer,#Modifiers VARCHAR(5), #DaysIn VARCHAR (2), #Enc_Rows int, #Mod varchar(3)
--Get count of encounters in Post Op period for current patient
SET #Enc_Rows = 0
SELECT #Enc_Rows = COUNT(*) FROM #PtPostOp
--Loop through records and concatenate rows
SET #Days = ''
WHILE #Enc_Rows > 0
BEGIN
--Get first record
SELECT TOP 1 #FromDate = FromDate
,#Modifiers = CASE
WHEN Modifiers LIKE '%RT%' THEN 'OD'
WHEN Modifiers LIKE '%LT%' THEN 'OS'
ELSE Modifiers
END
,#DaysIn = DaysIn
FROM #PtPostOp
ORDER BY FromDate DESC
--Concatenate Row
SET #Days = #Days + #Modifiers + ' ' + #DaysIn + ' days'
IF #Enc_Rows > 1
SET #Days = #Days + ': '
SET #Enc_Rows = #Enc_Rows -1
DELETE FROM #PtPostOp WHERE FromDate = #FromDate
END
IF #Days <> ''
SET #NewNote = ('**POST OP ' + #Days + ' ** ~' + #NotePart)
ELSE
SET #NewNote = #NotePart
--Update the PatientNote
UPDATE [Server1].[Database].[dbo].[Patients]
SET PatientNote = #NewNote
WHERE PatientChartNumber = #med_rec_nbr
END
As an addition to this - I'm also attempting to add a try - catch to my code, but that is generating DTC errors.

Generate Row number Automatically if only one Record is fetching

ALTER PROCEDURE [dbo].[SerailNo_LoadValue]
(
#SerialNo int,
#Season nchar(5)
)
AS
BEGIN
IF EXISTS (SELECT 1 FROM MATERIAL_Stock WHERE SerialNo = #SerialNo and Season = #Season)
BEGIN
if(#Season = 'PER')
BEGIN
SELECT material_code,ULC_id,setNo,MONTH(GETDATE())AS Delivery_Month,YEAR(GETDATE())AS Delivery_Year,1 AS NORECORD
,ISNULL(qty_total,0) AS qty_total,ISNULL(ULC,0) AS 'ulsmtr',isnull(MATNR,'') as MATNR --ADDED BY LALIT AS DISCUSSED WITH DIPENDRA ON 22 APRIL 2016
FROM MATERIAL_Stock LEFT JOIN ULC_master ON ULC_master.ID =MATERIAL_Stock.ULC_id WHERE SerialNo = #SerialNo and Season = #Season
END
ELSE
BEGIN
SELECT material_code,ULC_id,setNo,Delivery_Month,Delivery_Year,1 AS NORECORD
,ISNULL(qty_total,0) AS qty_total,ISNULL(ULC,0) AS 'ulsmtr',isnull(MATNR,'') as MATNR --ADDED BY LALIT AS DISCUSSED WITH DIPENDRA ON 22 APRIL 2016
FROM MATERIAL_Stock LEFT JOIN ULC_master ON ULC_master.ID =MATERIAL_Stock.ULC_id WHERE SerialNo = #SerialNo and Season = #Season
END
select distinct Delivery_Year, Delivery_Month, Season into #T from Material_stock
where Season=#Season
order by Delivery_Year,Delivery_Month
select *,ROW_NUMBER() OVER (ORDER BY Delivery_Year,Delivery_Month) AS RowNumber into #TT from #T
declare #MONTH int
declare #YEAR int
declare #ROWID int
select top 1 #MONTH=Delivery_Month,#YEAR=Delivery_Year from dbo.Material_stock where Season=#Season And SerialNo=#SerialNo
select #ROWID=RowNumber from #TT where Delivery_Month=#MONTH and Delivery_Year=#YEAR
select Delivery_Year as Delivery_Month1 , Delivery_Month, Delivery_Year, Season,RowNumber from #TT where RowNumber>=#ROWID
drop table #T
drop table #TT
END
END

SQL query to count number of records within a given timeframe, filling in the gaps

I am trying to count the number of records that fall between a given time period, generally 15 minutes, but I'd like to have this interval a variable. My table has a datetime column and I need to get a count of the number of records for every 15 minute interval between two dates and when there aren't any records for the 15-minute window, I need that time period with a zero. I've tried using CTE in different combinations and couldn't get it to work. I can generate the date series using a CTE, but haven't been able to get the actual data in the result. I have a working solution using a stored procedure with a WHILE loop. I was hoping to avoid this if possible in favor or a more elegant solution.
Here is my working loop using a temporary table:
declare #record_count int = 0
declare #end_date_per_query datetime
create table #output (
SessionIdTime datetime,
CallCount int)
while #date_from < #date_to
begin
set #end_date_per_query = DATEADD(minute, #interval, #date_from)
select #record_count = COUNT(*) from tbl WHERE SessionIdTime between #date_from and #end_date_per_query
insert into #output values (#date_from, #record_count)
set #date_from = #end_date_per_query
end
select * from #output order by sessionIdTime
drop table #output
Hopefully someone can help with a more elegant solution. Any help is appreciated.
A CTE works just fine:
-- Parameters.
declare #Start as DateTime = '20120901'
declare #End as DateTime = '20120902'
declare #Interval as Time = '01:00:00.00' -- One hour. Change to 15 minutes.
select #Start as [Start], #End as [End], #Interval as [Interval]
-- Sample data.
declare #Sessions as Table ( SessionId Int Identity, SessionStart DateTime )
insert into #Sessions ( SessionStart ) values
( '20120831 12:15:07' ), ( '20120831 21:51:18' ),
( '20120901 12:15:07' ), ( '20120901 21:51:18' ),
( '20120902 12:15:07' ), ( '20120902 21:51:18' )
select * from #Sessions
-- Summary.
; with SampleWindows as (
select #Start as WindowStart, #Start + #Interval as WindowEnd
union all
select SW.WindowStart + #Interval, SW.WindowEnd + #Interval
from SampleWindows as SW
where SW.WindowEnd < #End
)
select SW.WindowStart, Count( S.SessionStart ) as [Sessions]
from SampleWindows as SW left outer join
#Sessions as S on SW.WindowStart <= S.SessionStart and S.SessionStart < SW.WindowEnd
group by SW.WindowStart
Is this what you're looking for?
-- setup
DECLARE #interval INT, #start DATETIME
SELECT #interval = 15, #start = '1/1/2000 0:00:00'
DECLARE #t TABLE (id INT NOT NULL IDENTITY(1,1) PRIMARY KEY, d DATETIME)
INSERT INTO #t (d) VALUES
(DATEADD(mi, #interval * 0.00, #start)) -- in
,(DATEADD(mi, #interval * 0.75, #start)) -- in
,(DATEADD(mi, #interval * 1.50, #start)) -- out
-- query
DECLARE #result INT
SELECT #result = COUNT(*) FROM #t
WHERE d BETWEEN #start AND DATEADD(mi, #interval, #start)
-- result
PRINT #result

Cursor never ending in T-SQL

I have problems with a cursur that’s ends randomly. I want to produce a row for each element , so for every element there is a row for every month in 2012. If there is a row missing for a specific month, it should create that as well.
Now, it’s producing a row for the last element in every month until year 2057.
Now, I have a row for each element until in every month until year 2057 and only for one element in my table.
My table design:
create table tblDataUpdateTest
(
slno int identity(1,1),
cName varchar(50),
cRemarks varchar(50),
[Date] date,
)
insert into tblDataUpdateTest (cName, cRemarks,[Date]) values ('name1','Text1','2012-01-01'),
('name2','Text2','2012-01-01')
My code:
declare #y as int
declare #d as int
SET #y = 2012
SET #d = 1
Declare ##counter int
Declare ##month int
set ##counter=0
Declare ##slno int
Declare ##cRemarks varchar(100)
Declare ##cName varchar(50)
Declare ##Date date
set ##month = 1
Declare tmepTbl cursor
For
Select slno,cName,cRemarks,date from tblDataUpdateTest
Open tmepTbl /* Opening the cursor */
fetch next from tmepTbl
into ##slno,##cName,##cRemarks,##Date
while ##fetch_Status=-1
begin
if not exists (select cRemarks from tblDataUpdateTest where MONTH(Date) = ##month AND YEAR(Date) = 2012)
begin
insert into tblDataUpdateTest (cName, cRemarks,[Date]) values (##cName,'s',(DateAdd(yy, 2012-1900,DateAdd(m, ##month - 1, 01 - 1))))
end
fetch next from tmepTbl
into ##slno,##cName,##cRemarks,##Date
set ##month=##month+1
end
close tmepTbl
Deallocate tmepTbl
My table now
cName cRemarks Date
name1 Text1 2012-01-01
name2 Text2 2012-01-01
I want follwing to happen:
cName cRemarks Date
name1 Text1 2012-01-01
name1 Text1 2012-02-01
name1 Text1 2012-03-01
name2 Text2 2012-01-01
name2 Text2 2012-02-01
name2 Text2 2012-03-01
and so on.
Thanks!
Wouldn't be better to use a recursive CTE, instead of a cursor?
Here is an example:
DECLARE #y INT
DECLARE #m INT
DECLARE #n INT
SET #y = 2012
SET #m = 1
SET #n = 3
;WITH dates(d, m) AS(
SELECT CONVERT(DATE, CONVERT(VARCHAR(4), #y) + '-01-01'), 1
UNION ALL
SELECT CONVERT(DATE, CONVERT(VARCHAR(4), #y) + '-' + CASE WHEN m + 1 < 10 THEN '0' ELSE '' END + CONVERT(VARCHAR(2), m + 1) + '-01')
, m + 1
FROM dates
WHERE m < #n
)
INSERT INTO tblDataUpdateTest(cName, cRemarks,[Date])
SELECT cName, cRemarks, [date] FROM (
SELECT cName, cRemarks, dates.d AS [date]
FROM tblDataUpdateTest
CROSS JOIN dates) t
WHERE NOT(EXISTS(SELECT * FROM tblDataUpdateTest WHERE cName = t.cName AND [date] = t.[date]))
OPTION(MAXRECURSION 11)
SELECT * FROM tblDataUpdateTest ORDER BY cName

Split datetime SQL Server 2008

I have a table with 3 columns StartDate, EndDate, ElapsedTimeInSec.
I use an AFTER INSERT trigger to calculate the ElapsedTimeInSec.
I would like to do this:
If my start date is 2011-11-18 07:30:00 and my end date 2011-11-18 9:30:00 which give me a ElapsedtimeInSec of 7200 I would like to be able to split it this way.
Row 1 : 2011-11-18 07:30:00 / 2011-11-18 08:00:00 / 1800
Row 2 : 2011-11-18 08:00:00 / 2011-11-18 09:00:00 / 3600
Row 3 : 2011-11-18 09:00:00 / 2011-11-18 09:30:00 / 1800
How can I achieve this result ?
I dont think I made my explaination clear enough.
I have an actual table with data in it which as 2 field one with a StratDowntime and one with a EndDowntime and I would like to create a view of hours per hour base on a production shift of 12 hours (07:00:00 to 19:00:00) of the downtime.
So If I have a downtime from 2011-11-19 06:00:00 to 2011-11-19 08:00:00 I want in my report to see from 07:00:00 so the new rocrd should look like 2011-11-19 07:00:00 to 2011-11-19 08:00:00.
Another example if I do have downtime from 2011-11-19 10:30:00 to 2011-11-19 13:33:00 I should get in my report this
- 2011-11-19 10:30:00 to 2011-11-19 11:00:00
- 2011-11-19 11:00:00 to 2011-11-19 12:00:00
- 2011-11-19 12:00:00 to 2011-11-19 13:00:00
- 2011-11-19 13:00:00 to 2011-11-19 13:33:00
I hope this will clarify the question because none of the solution down there is actually doing this it is close but not on it.
thanks
You could try something like:
DECLARE #StartDate DATETIME = '11/18/2011 07:30:00',
#EndDate DATETIME = '11/18/2011 09:30:00',
#Runner DATETIME
IF DATEDIFF (mi, #StartDate, #EndDate) < 60
BEGIN
SELECT #StartDate,
#EndDate,
DATEDIFF (s, #StartDate, #EndDate)
RETURN
END
SET #Runner = CONVERT (VARCHAR (10), #StartDate, 101) + ' ' + CAST (DATEPART(hh, #StartDate) + 1 AS VARCHAR) + ':00:00'
WHILE #Runner <= #EndDate
BEGIN
SELECT #StartDate,
#Runner,
DATEDIFF (s, #StartDate, #Runner)
SET #StartDate = #Runner
SET #Runner = DATEADD(hh, 1, #Runner)
END
SET #Runner = CONVERT (VARCHAR (10), #EndDate, 101) + ' ' + CAST (DATEPART(hh, #EndDate) AS VARCHAR) + ':00:00'
SELECT #Runner,
#EndDate,
DATEDIFF (s, #Runner, #EndDate)
CTE:
DECLARE #beginDate DATETIME,
#endDate DATETIME
SELECT #beginDate = '2011-11-18 07:30:00',
#endDate = '2011-11-18 09:33:10'
DECLARE #mytable TABLE
(
StartDowntime DATETIME,
EndDowntime DATETIME,
ElapsedDowntimesec INT
)
-- Recursive CTE
;WITH Hours
(
BeginTime,
EndTime,
Seconds
)
AS
(
-- Base case
SELECT #beginDate,
DATEADD(MINUTE, ( DATEPART(MINUTE, #beginDate) * -1 ) + 60, #beginDate),
DATEDIFF
(
SECOND,
#beginDate,
CASE
WHEN #endDate < DATEADD(MINUTE, ( DATEPART(MINUTE, #beginDate) * -1 ) + 60, #beginDate) THEN #endDate
ELSE DATEADD(MINUTE, ( DATEPART(MINUTE, #beginDate) * -1 ) + 60, #beginDate)
END
)
UNION ALL
-- Recursive
SELECT Hours.EndTime,
CASE
WHEN #endDate < DATEADD(MINUTE, ( DATEPART(MINUTE, Hours.BeginTime) * -1 ) + 120, Hours.BeginTime) THEN #endDate
ELSE DATEADD(minute, ( DATEPART(MINUTE, Hours.BeginTime) * -1 ) + 120, Hours.BeginTime)
END,
DATEDIFF
(
SECOND,
Hours.EndTime,
CASE
WHEN #endDate < DATEADD(MINUTE, ( DATEPART(MINUTE, Hours.BeginTime) * -1 ) + 120, Hours.BeginTime) THEN #endDate
ELSE DATEADD(MINUTE, ( DATEPART(MINUTE, Hours.BeginTime) * -1 ) + 120, Hours.BeginTime)
END
)
FROM Hours
WHERE Hours.BeginTime < #endDate
)
INSERT INTO #myTable
SELECT *
FROM Hours
WHERE BeginTime < #endDate
SELECT * FROM #myTable
Results
BeginTime EndTime Seconds
2011-11-18 07:30:00.000 2011-11-18 08:00:00.000 1800
2011-11-18 08:00:00.000 2011-11-18 09:00:00.000 3600
2011-11-18 09:00:00.000 2011-11-18 09:33:10.000 1990
You can use a table valued function applied like SELECT * FROM [dbo].split('2011-11-02 12:55:00','2011-11-02 13:05:00')
Function defintion:
CREATE FUNCTION [dbo].[split] (#d1 DATETIME, #d2 DATETIME)
RETURNS #result TABLE (
StartDate DATETIME,
EndDate DATETIME,
ElapsedTimeSeconds INT
)
AS
BEGIN
-- Store intermediate values in #tmp, using ix as driver for start times.
DECLARE #tmp TABLE (ix INT NOT NULL IDENTITY(0,1) PRIMARY KEY
, d1 DATETIME, d2 DATETIME)
-- Insert first hole hour lower than start time
INSERT INTO #tmp (d1) SELECT DATEADD(HOUR, DATEDIFF(HOUR, -1, #d1), -1)
-- Calculate expected number of intervals
DECLARE #intervals INT = DATEDIFF(HOUR, #d1, #d2) - 1
-- insert all intervals
WHILE #intervals > 0
BEGIN
INSERT INTO #tmp (d1, d2) select top 1 d1, d2 FROM #tmp
SET #intervals = #intervals - 1
END
-- Set start and end time for all whole hour intervals
UPDATE #tmp SET d1 = DATEADD(hour, ix, d1)
, d2 = DATEADD(hour, ix + 1, d1)
-- Set correct start time for first interval
UPDATE #tmp SET d1 = #d1 WHERE d1 <= #d1
-- Insert end interval
INSERT INTO #tmp (d1, d2)
SELECT MAX(d2), #d2 FROM #tmp
-- Delete non-empty last interval
DELETE FROM #tmp WHERE d1 = d2
-- Insert #tmp to #result
INSERT INTO #result (StartDate, EndDate)
SELECT d1, d2 FROM #tmp
-- Set interval lengths
UPDATE #result SET ElapsedTimeSeconds = DATEDIFF(second, StartDate, EndDate)
return
END
GO
To get a result from an existing table, you can use CROSS APPLY. Assuming a table YourTable with StartTime and EndTime you can do something like
SELECT s.*, y.* FROM YourTable y
cross apply dbo.split(y.StartTime, y.EndTime) s
WHERE y.EndTime < '2011-09-11'
to get a result with a kind of join between input data and output table.