Count Number of hours between a range SQL - sql

Im trying to count the number of working hours from some users for the month, the only way to do it is to see the date and hour for the productions entries they made. so I got:
Select a.EmployeeID, cast(a.fDate as Date) fDate, datepart(HOUR, a.fDate) fHour
From Table
Group by a.EmployeeID, cast(a.fDate as Date), datepart(HOUR, a.fDate)
This gives me the detail by date of the hours production was done, however I would like to know the total of hours in that time frame. For example:
I know employee xxx on 09/01/2018 registered production at 10:00AM, 11:00AM 12:00PM and 1:00PM, so the result will be 4 hours. Every employee has different working days so thats the only way I think I can calculated this...
Help?

Can you use the DATEDIFF of the min and max of fDate, group by EmployeeId and Date of a.fDate?
For example:
Select a.EmployeeID, cast(a.fDate as Date) as fDate, min(a.fDate) as TimeIn, max(a.fDate) as TimeOut, (DATEDIFF(mi, min(a.fDate), max(a.fDate)) / 60) as Hours
From MyTable a
Group by a.EmployeeID, cast(a.fDate as Date)

Related

How to get average of earliest datetime per day?

I need to get average time based on their earliest check in for each day.
For example, these are my check in datetime.
Person | Checkin
-----------------
Jack 2022-01-06 16:42:34.000
Jack 2022-01-06 17:30:34.000
Jack 2022-01-07 10:22:34.000
Jack 2022-01-07 12:12:54.000
Jack 2022-01-08 11:08:53.000
When I want to calculate my average check in time based on earliest check in, I should only consider these datetime.
2022-01-06 16:42:34.000
2022-01-07 10:22:34.000
2022-01-08 11:08:53.000
This is my current sql to get the average check in time. But this consider all the datetime above and gets the average time.
Select Person,(CAST(DATEADD(SS, AVG(CAST(DATEDIFF(SS, '00:00:00', CAST(Checkin AS TIME)) AS BIGINT)), '00:00:00' ) AS TIME)) AS 'AvgCheckInTime' from #Tab_CheckIn
group by Person
How can I only consider the earliest datetime for each day and get the average time?
This is a little messy.
Firstly you need to group the data to get the earliest checkin my day. That's quite simple, using CONVERT to date and MIN.
Then you need to get the "average" of those times. You can't average a time in SQL Server, however, as time represents a point in time during the day not an interval, so it doesn't make sense to average them like it would a timespan. As a result you need to "convert" the values to a numerical value.
In this case I use DATEDIFF to get the number of seconds since midnight to the checkin converted to a time. Then I can average those values, and finally add those second to midnight:
WITH Earliest AS(
SELECT Person,
MIN(Checkin) AS EarliestCheckin
FROM dbo.YourTable
GROUP BY Person,
CONVERT(date,Checkin))
SELECT Person,
DATEADD(SECOND,AVG(DATEDIFF(SECOND,'00:00',CONVERT(time,EarliestCheckin))),CONVERT(time,'00:00')) AS AverageCheckin
FROM Earliest
GROUP BY Person;
Assuming you get desired result(except filtering the undesired dates)
Select t.Person,(CAST(DATEADD(SS, AVG(CAST(DATEDIFF(SS, '00:00:00', CAST(t.Checkin AS TIME)) AS BIGINT)), '00:00:00' ) AS TIME)) AS 'AvgCheckInTime'
from (SELECT Person, min(Checkin) as Checkin FROM #Tab_CheckIn
GROUP BY Person, cast(Checkin As Date)) t
group by t.Person
SELECT Person,(CAST(DATEADD(SS, AVG(CAST(DATEDIFF(SS, '00:00:00', CAST(Checkin AS TIME)) AS BIGINT)), '00:00:00' ) AS TIME)) AS 'AvgCheckInTime'
from (
SELECT person, checkin, row_number() over(PARTITION by person, CAST(checkin as DATE) order by checkin asc) as row
FROM #Tab_CheckIn) as p
where p.row =1
group by p.person

SQL to count records per hour and count how many hours that total record count was greater than a value

Using SQL Server 2014. I have a list of records that are time stamped and I would like to count how many records there are per hour then count how many hours each day that the record count exceeded a given number, say 20.
Here's what I have so far:
select count(distinct datepart(hour, convert(datetime, OrderStateDate))) Count_HoursOver,
datepart(YEAR, convert(datetime, OrderStateDate)) Date_YEAR,
datepart(month, convert(datetime, OrderStateDate)) Date_MONTH,
datepart(day, convert(datetime, OrderStateDate)) Date_DAY
from Reporting.dbo.Orders
group by datepart(YEAR, convert(datetime, OrderStateDate)),
datepart(month, convert(datetime, OrderStateDate)),
datepart(day, convert(datetime, OrderStateDate))
having count(idscript) >= 20
The results aren't correct and I can't make sense of what's being returned and why. Am I using HAVING incorrectly? Any advice would be appreciated!
You kind of have a 2-part question here
I would like to count how many records there are per hour
You can create a query that returns tuples (RecordsPerHour,HOUR,YEAR,MONTH,DAY) as follows:
SELECT
count(*) as RecordsPerHour,
datepart(hour,convert(datetime,OrderStateDate)) as Date_HOUR,
datepart(year,convert(datetime,OrderStateDate)) as Date_YEAR,
datepart(month,convert(datetime,OrderStateDate)) as Date_MONTH,
datepart(day,convert(datetime,OrderStateDate)) as Date_DAY
FROM Reporting.dbo.Orders
GROUP BY
datepart(year,convert(datetime,OrderStateDate)),
datepart(month,convert(datetime,OrderStateDate)),
datepart(day,convert(datetime,OrderStateDate)),
datepart(hour,convert(datetime,OrderStateDate))
then count how many hours each day that the record count exceeded a given number, say 20
To do this, use the query from the first part of your question in a nested query, using a HAVING clause to filter only hours that contain at least 20 orders.
On the outer query, group by (YEAR,MONTH,DAY) to determine the number of hours in that day with at least 20 orders:
SELECT
count(*) as HoursWithAtLeast20Orders,
Date_YEAR,
Date_MONTH,
Date_DAY
FROM
(SELECT
datepart(hour,convert(datetime,OrderStateDate)) as Date_HOUR,
datepart(year,convert(datetime,OrderStateDate)) as Date_YEAR,
datepart(month,convert(datetime,OrderStateDate)) as Date_MONTH,
datepart(day,convert(datetime,OrderStateDate)) as Date_DAY
FROM Reporting.dbo.Orders
GROUP BY
datepart(year,convert(datetime,OrderStateDate)),
datepart(month,convert(datetime,OrderStateDate)),
datepart(day,convert(datetime,OrderStateDate)),
datepart(hour,convert(datetime,OrderStateDate))
HAVING count(*) >=20) as t
GROUP BY
Date_YEAR,
Date_MONTH,
Date_DAY
First round to hour then round to day
SELECT
count(*) as [hours with 20+ Orders],
dateadd(day, datediff(day,'20000101',dt_hour_rounded),'20000101') as dt_day_rounded
FROM (
SELECT
count(*) as OrdersInHour,
dateadd(hour, datediff(hour,'20000101',OrderStateDate),'20000101') as dt_hour_rounded
FROM Reporting.dbo.Orders
GROUP BY dateadd(hour, datediff(hour,'20000101',OrderStateDate),'20000101')
) t
GROUP BY dateadd(day,datediff(day,'20000101',dt_hour_rounded),'20000101')
WHERE OrdersInHour >= 20

SQL Count of date values that dont match hour and day in select statement

I have a problem unique to a business process. My user needs to know how many dates, counted, are before a specific end time that do not match on the hour or the day.
Here is an example.
AAA, 2016-03-15 16:00:28.967, 2016-03-15 16:02:58.487, 2016-03-17 14:01:24.243
In the example above id AAA has 3 entries. I need to count only the ones that don't have a matching hour and day. So the actual count should come out to be 2.
I have to do this all in SQL and can't use a CTE. It needs to be either a sub select or some type of join.
Something like this.
SELECT id, date, (
SELECT COUNT(*)
FROM x
WHERE day!=day
AND hour!=hour AND date < z
) AS DateCount
Results would be AAA, 2
I am thinking some type of recursive comparison but I am not sure how to accomplish this without a CTE.
In SQL Server you can try something like this:
SELECT id, CONVERT(VARCHAR(13), [date], 120) AS [Date], COUNT(*) AS DateCount
FROM YourTable
WHERE [date] < #ENDDATE
GROUP BY id, CONVERT(VARCHAR(13), [date], 120)
SELECT a AS current_a, COUNT(*) AS b,day AS day, hour as hour,
(SELECT COUNT(*)
FROM t
WHERE day != day
AND hour != hour
AND date < z ) as datecount
FROM t GROUP BY a ORDER by b DESC

SQL Query to find difference in hours between first and last records of each day for a month, and sum each difference to find the total

This isn't the easiest query to describe in one sentence which is why the title might not be the best.
Basically I have a log of sign-ins. This log looks like:
ContactId, LocationId, TimeStamp
These represent a Contact (registered person) being seen at a location at a given time.
What I want to do it pick a datetime window, and for each day in that window I want to take the first sign-in, and the last sign-in and work out the difference in hours. This difference in hours should then contribute to an overall total, giving an estimate of the total time the Contact spent at the location during the month.
Days when the Contact was not seen should obviously be ignored, and days when the Contact was seen only once should not be used either as no difference can be calculated.
I know how to find the difference between two datetimes in hours:
select DATEDIFF(HOUR, datetime1, datetime2) as hoursestimate
But I'm not sure how to:
Make datetime1 and datetime2 the first and last record of a day.
Loop through the data increasing the day count to find a cumulative total of hours (TotalHours) over the month.
Once I have the cumulative hours total over the time period based on the first and last seen difference I will perform a join with my contact table to obtain their first and last name and order by this TotalHours column, so hopefully the final table I want to be returned from the query will be something like:
Id FirstName LastName TotalHours
35 Bob Bobberson 65
40 Jim Jimmerson 63
2 Harry Harrison 54
It's steps 1 and 2 that I'm a bit lost on however. Any thoughts?
I think you want two aggregations:
select contactid, sum(hoursestimate) as total
from (select contactid, cast(TimeStamp as date) as dte,
DATEDIFF(HOUR, min(TimeStamp), max(TimeStamp)) as hoursestimate
from t
where TimeStamp >= #start and TimeStamp < #end
group by contactid, cast(TimeStamp as date)
) t
group by contactid;
There are couple of different techniques that can help you here.
CAST will let you strip the date and time sections of the date time stamp from each other. This is really handy for grouping multiple records from the same day, but different times, together.
The HAVING can be used to filter out contacts with only record in a day. This works by counting the number of contributing records, and removing those below your threshold.
This example combines those techniques:
Exmaple
WITH SampleDate AS
(
/* Lets make some records to experiment with.
*/
SELECT
r.*
FROM
(
VALUES
(1, 1, '2015-01-01 09:00:00.000'),
(1, 1, '2015-01-01 12:00:00.000'),
(1, 1, '2015-01-01 17:00:00.000'),
(2, 1, '2015-01-01 09:00:00.000')
) AS r(ContactId, LocationId, [TimeStamp])
)
SELECT
ContactId,
LocationId,
CAST([TimeStamp] AS DATE) AS [Day],
MIN(CAST([TimeStamp] AS TIME)) AS FirstSeenTime,
MAX(CAST([TimeStamp] AS TIME)) AS LastSeenTime,
DATEDIFF(
HOUR,
MIN(CAST([TimeStamp] AS TIME)),
MAX(CAST([TimeStamp] AS TIME))
) AS HoursEstimate
FROM
SampleDate
GROUP BY
ContactId,
LocationId,
CAST([TimeStamp] AS DATE) -- Removing the time allows us to create 1 record per day.
HAVING
COUNT(*) > 1 -- Make sure we've seen the contact at least twice.
;

SQL - returning average number of items by days of the week.

The query I have below works fine and returns the items shipped, total days, and average number of items per day.
SELECT A.ItemsShipped, A.TotalDays, cast(A.ItemsShipped as float)/cast(A.TotalDays as float) AS
'Averange items shipped per day'
FROM(SELECT
(SELECT Count(*)
FROM PARTNER_WORKORDER
WHERE statusNo = 110)
AS ItemsShipped,
(SELECT DATEDIFF(day, MIN(orderDt),'2013-11-20 00:00:00.000')
From PARTNER_WORKORDER)
AS TotalDays
)A
The question I have now is, how can I get this return the average number of items shipped per each day of the week (average on Monday, average on Tuesday, etc...) so that it ends up returning seven results. I know there is the way of doing a query for each day, but what would be the efficient way of doing it all in one query?
I am using MS SQLServer for this project.
Thank for any help anyone can give.
This should solve your problem. I don't have SQL Server instance at hand (so can't validate it), but I guess you get the idea.
WITH OrdersPerDay AS (
SELECT
CAST(orderDt as INTEGER) as OrderDay,
COUNT(1) as OrderCount
FROM PARTNER_WORKORDER
GROUP BY CAST(orderDt as INTEGER)
WHERE statusNo = 110
)
SELECT
DATEPART(dw, CAST(OrderDay as DATETIME)) as WeekDay,
AVG(OrderCount)
FROM OrdersPerDay
GROUP BY DATEPART(dw, CAST(OrderDay as DATETIME))