I have a Bookings table and I need to calculate the total hours a staff member has worked in a week, but I need it to cut off dates between Monday midnight and the following Monday at midnight.
create table Bookings (ID int IDENTITY(1,1) not null, start datetime, finish datetime, staffId int)
insert into Bookings (start, finish, staffId) values ('2022-06-19 21:00:00', '2022-06-20 07:00:00', 1)
insert into Bookings (start, finish, staffId) values ('2022-06-24 21:00:00', '2022-06-25 07:00:00', 1)
insert into Bookings (start, finish, staffId) values ('2022-06-25 21:00:00', '2022-06-26 07:00:00', 1)
insert into Bookings (start, finish, staffId) values ('2022-06-26 21:00:00', '2022-06-27 07:00:00', 1)
select *, datediff(MINUTE, start, finish)/60.0
from Bookings
where staffid = 1 and start between '2022-06-19' and '2022-06-27'
I need row 1 to start at 2022-06-20 00:00 and row 4 to finish at 2022-06-27 00:00 so the hours for row 1 would be 7, and for row 4 would be 3, thus totaling 30 hours instead of 40.
Any ideas on how to do this?
I suspect what you need to do here is firstly change your WHERE to look at the finish time being after your start value, and the start time being before your finish value. Then for the hours worked, you need to use a CASE expression to return the column or input parameters value, depending which is lesser/greater:
DECLARE #Start date = '20220620',
#Finish date = '20220627';
SELECT ID,
staffId,
finish,
staffId,
DATEDIFF(MINUTE,CASE WHEN start < #Start THEN #Start ELSE start END,CASE WHEN finish > #Finish THEN #Finish ELSE finish END) / 60. AS Hours
FROM dbo.Bookings
WHERE staffId = 1
AND finish > #Start
AND start < #Finish;
Related
I have a table with job schedules :
job_id [unique ID]
pref_start [date]
spec_duration [time in seconds]
I can calculate the end date from the preferred start and duration. The pref_start is not fixed, and can be changed at whim by the engineers.
I need to report activity in any given week, so if I have data similar to:
jid start end
J1 01/01/yyyy 15/02/yyyy
J2 07/01/yyyy 08/02/yyyy
J3 09/02/yyyy 21/03/yyyy
How would I query "tell me the job id's that occur on each day of the week 07/02/yyyy to 12/02/yyyy"
First find the matching intervals between your jobs and your filtering interval, then the amount of days for the filter interval and the overlapping intervals must match:
DECLARE #Jobs TABLE (
ID INT IDENTITY,
StartDate DATE,
EndDate DATE)
INSERT INTO #Jobs (
StartDate,
EndDate)
VALUES
('2019-01-01', '2019-02-15'),
('2019-01-07', '2019-02-08'),
('2019-02-09', '2019-03-21')
DECLARE #FilterStartDate DATE = '2019-02-07'
DECLARE #FilterEndDate DATE = '2019-02-12'
;WITH AtLeast1DayOverlappingJobs AS
(
SELECT
J.ID,
J.StartDate,
J.EndDate,
OverlappingStartDate = CASE
WHEN J.StartDate > #FilterStartDate THEN J.StartDate ELSE #FilterStartDate END, -- Highest of 2
OverlappingEndDate = CASE
WHEN J.EndDate < #FilterEndDate THEN J.EndDate ELSE #FilterEndDate END -- Lowest of 2
FROM
#Jobs AS J
WHERE
-- They share at least 1 day
#FilterStartDate <= J.EndDate AND #FilterEndDate >= J.StartDate
)
SELECT
T.*
FROM
AtLeast1DayOverlappingJobs AS T
WHERE
-- Amount of days must match between filter and overlapping periods
DATEDIFF(DAY, #FilterStartDate, #FilterEndDate) = DATEDIFF(DAY, T.OverlappingStartDate, T.OverlappingEndDate)
Results:
ID StartDate EndDate OverlappingStartDate OverlappingEndDate
1 2019-01-01 2019-02-15 2019-02-07 2019-02-12
I need to return count on results per day between a time range that will overlap days.
So from 8 PM to 8 AM for every day that starts on M-F return a count for that time period. And do that for the entire year.
This is what I have, and I could do a simple and between if I only wanted on day but I'm not sure how to iterate through days especially when the start is on one day and the end on the next but skip the ones that Start on a Saturday or Sunday.
SELECT TOP (50)
ClientVisit.visittype,
ClientVisit.location_id,
ClientVisit.visittype_id,
Location.location_desc,
Location.location_code,
ClientVisit.timein,
ClientVisit.timeout,
ClientVisit.visit_dateday
FROM
ClientVisit
INNER JOIN
Location ON ClientVisit.location_id = Location.location_id
WHERE
(ClientVisit.visittype Like '%Open Chart%'
OR ClientVisit.visittype LIKE '%Diag%')
AND (Location.location_code = 'Access-505'
OR Location.location_code = 'Access-hosp')
AND (ClientVisit.timein BETWEEN #param1 AND #param2)
Filtering days of week and hours of the day is easy enough. Does this group by get at what you're trying to accomplish for the counts?
SELECT CAST(ClientVisit.timein AS DATE) AS DT, COUNT(*)
FROM ClientVisit INNER JOIN Location
ON ClientVisit.location_id = Location.location_id
WHERE
(ClientVisit.visittype Like '%Open Chart%' OR ClientVisit.visittype LIKE '%Diag%')
AND (Location.location_code = 'Access-505' OR Location.location_code = 'Access-hosp')
-- Use date params rather than datetime
AND CAST(ClientVisit.timein AS DATE) BETWEEN #param1 AND #param2
-- M-F assuming ##DATEFIRST is Sunday (7)
AND DATEPART(weekday, ClientVisit.timein) BETWEEN 2 AND 6
-- time of day. won't include the instant of 8:00:00am
AND ( DATEPART(hour, ClientVisit.timein) BETWEEN 8 AND 23
OR DATEPART(hour, ClientVisit.timein) BETWEEN 0 AND 7)
GROUP BY CAST(ClientVisit.timein AS DATE);
If you need to treat the hours from 8PM to 8AM as a single shift then you can adjust the times prior to so that times after midnight are treated as part of the preceeding day:
WITH AdjustedVisit AS (
SELECT *, DATEADD(hour, -8, timein) AS adjustedin FROM ClientVisit)
-- Use date params rather than datetime
WHERE CAST(timein AS DATE) BETWEEN #param1 AND #param2
)
SELECT CAST(v.adjustedin AS DATE) AS DT, COUNT(*)
FROM AdjustedVisit AS v INNER JOIN Location AS l
ON v.location_id = l.location_id
WHERE
(v.visittype Like '%Open Chart%' OR v.visittype LIKE '%Diag%')
AND (l.location_code = 'Access-505' OR l.location_code = 'Access-hosp')
-- M-F assuming ##DATEFIRST is Sunday (7)
AND DATEPART(weekday, v.adjustedin) BETWEEN 2 AND 6
-- time of day. won't include the instant of 8:00:00am
AND DATEPART(hour, v.adjustedin) BETWEEN 12 AND 23
GROUP BY CAST(v.adjustedin AS DATE);
In SQL Server, how many 8 day periods do I have between this two dates?
#date1 = '2016/11/08'
#date2 = '2017/02/10'
Manually I have four 8 day periods between those dates
Declare #date1 date = '2016/11/08'
Declare #date2 date = '2017/02/10'
Select count(*)
From (
Select Top (DateDiff(DD,#date1,#date2)+1) D=cast(DateAdd(DD,Row_Number() Over (Order By (Select null))-1,#date1) as Date)
From master..spt_values n1, master..spt_values n2
) A
Where DatePart(DAY,D)=8
Returns
4
Here's an alternate approach using the standard SQL Date functions:
declare #StartDate datetime = '2016-11-08'
, #EndDate datetime = '2023-10-09' --'2017-02-10'
, #dayOfInterest int = 8 --in case you decided you were interested in a different date
--if you don't care whether or not start date is before or after end date; swap their values...
/*
select #StartDate = min(d)
, #EndDate = max(d)
from (select #StartDate d union select #EndDate) x
*/
select
case when #StartDate <= #EndDate then
--if the end date is after or on the start date, we count the number of eighths in between
datediff(month,#StartDate,#EndDate) --count the number of months between the two dates
+ case when datepart(dd,#StartDate)<=#dayOfInterest then 0 else -1 end --if our range excludes the nth this month, exclude it
+ case when datepart(dd,#EndDate)<#dayOfInterest then 0 else 1 end --if our range includes the nth on the last month, include it
else
0 --if the end date is before the start date; we return 0
end
CountTheDays --give the output column a name
Caution
NB: For the 8th this will work perfectly.
For dates after 28th it isn't reliable (you'd be better off going with John Cappelletti's solution), as my logic would add 1 day per month, regardless of whether that month contained that day (i.e. if counting the number of 30ths between 1st Jan and 1st March you'd get 2 instead of 1, as my logic assumes Feb 30th to be a valid date.
I have a table whose only date fields are smalldatetime and DOW (day of week 1=Sunday).
I need to aggregate these days for weekly averages for specific week/year combinations (without hours and minutes per below script).
How can I convert the smalldate and DOW into a year and week#? I have a table I can link to that uses Saturday (DOW 7) as a WeekEndDate that I could link to if I could get that far. Any help would be greatly appreciated.
For example this statement provides the below results:
select a.LOB_Code, a.store, day, DOW, hour, minutes, (sum(NetTrans)/1.000)sumNetTrans
from facts.Store_Day_TransBy30 a
inner join facts.Stores b on a.Store = b.Store
where A.store = 8169 and DAY between '1/3/2016' and '1/4/2016'
group by a.LOB_Code, a.store, day, DOW, hour, minutes
order by a.LOB_Code, a.store, day, DOW, hour, minutes
enter image description here
Also here is the another query with temp table and manually generated data. It's always recommended to provide some sample data to get hold off complete picture.
SET NOCOUNT ON;
CREATE TABLE DemoTable
(
ID INT IDENTITY(1,1) NOT NULL,
DateCreated SMALLDATETIME,
DOW TINYINT NOT NULL, -- This can be generated runtime(DATEPART(DW,date column)) but as your table contain thsi column I've purposefully added here.
TmpCount INT NOT NULL -- Random column used for aggregation
)
-- Populate some records
DECLARE #CurrentDate DATE='2015-01-01'
WHILE #CurrentDate <='2016-12-31'
BEGIN
INSERT DemoTable
(
DateCreated,
DOW,
TmpCount
)
SELECT
#CurrentDate AS DateCreated,
DATEPART(DW,#CurrentDate) AS DOW,
ABS(Checksum(NewID()) % 6) + 1 AS TmpCount
SET #CurrentDate=DATEADD(dd,1,#CurrentDate)
END
-- Retrieve aggregated data
SELECT
YEAR(DateCreated) AS Year#
,DATEPART(WK,DateCreated) AS Week#
,SUM(TmpCount) AS TmpSum
FROM DemoTable
GROUP BY YEAR(DateCreated),DATEPART(WK,DateCreated)
ORDER BY 1,2
-- Drop Table
DROP TABLE DemoTable
I assume day column is of datatype, here is the query which may help you.
SELECT
a.LOB_Code,
a.store,
day,
DOW,
hour,
minutes,
(sum(NetTrans)/1.000)sumNetTrans,
YEAR([day]) AS Year#,
DATEPART(WK,[day]) AS Week#
from facts.Store_Day_TransBy30 a
inner join facts.Stores b on a.Store = b.Store
where A.store = 8169 and DAY between '1/3/2016' and '1/4/2016'
group by a.LOB_Code, a.store, day, DOW, hour, minutes,YEAR([day]),DATEPART(WK,[day])
order by a.LOB_Code, a.store, day, DOW, hour, minutes
I need to get the number of elapsed days between any two dates with respect to the current date. IE:
mm/dd/yyyy
Current day = 07/10/2015
07/08/2013 ... 07/11/2013 - 4 days elapsed
Current day = 07/10/2015
07/08/2015 ... 07/11/2015 - 2 days have elapsed
I've tried several combinations using DATEDIFF with day as the date part, however, I can't seem to get a clean way to get the days elapsed when the date could be past or present.
EDIT
I know the start date and the end date of a certain business process. They could be this year, last year, two years ago and so on. I need a way via SQL Server functions to figure out the days total elapsed. If it's not the current year, obviously the entire span/range would have elapsed. If it's the current year, perhaps the entire span/range hasn't elapsed and it needs to say how many days are "into the process" based on the respected start time, end time and current time.
Hopefully this makes more sense?
Please help.
I used #Sean Lange, with a small tweak:
DATEDIFF(DAY, #StartDate, case when #EndDate < GETDATE() then #EndDate + 1 else GETDATE() end)
Thanks all.
This is pretty similar to the answer provided by Stan but here is my take on this.
with Something as
(
select CAST('2013-07-08' as datetime) as StartDate
, CAST('2013-07-11' as datetime) as EndDate
union all
select '2015-07-08', '2015-07-11'
)
select *
, DATEDIFF(DAY, StartDate, case when EndDate < GETDATE() then EndDate else GETDATE() end)
from Something
How about this:
Given:
CREATE TABLE dbo.test ( ChildID INT Identity,
Start DateTime
, Finish DateTime
)
and your test data:
insert into dbo.test (start,finish) values('07/08/2013','07/11/2013')
insert into dbo.test (start,finish) values('07/08/2015','07/11/2015')
then
select start,finish
, DATEDIFF(DAY, start, CASE WHEN GETDATE() BETWEEN start and finish
THEN GETDATE() - 1 ELSE finish END) + 1 as elapsed
from dbo.test
gives the result from your example.
You might have to tweak if there are other adjustments for how the current date fits between the range.