Sql to get number of hours per user per day - mysql

A table 'Log' has the below columns:
SystemName
User
Datetime - it's a timestamp
Status - has the values Start or Stop.
I need to write a query which will give me :
Number of hours spent per user per day on system X.
Please see example data below:
X, Amit, 05/01/2019 08:45:00, Start
X, Amit, 05/03/2019 13:25:00, Stop
X, Dave, 05/01/2019 09:10:35, Start
X, Dave, 05/01/2019 17:35:42, Stop
Output:
Amit,05/01/2019, 15h
Amit,05/02/2019, 24h
Amit,05/03/2019, 9h
Dave,05/01/2019, 8h
My approach till now :
I was thinking I could use lead or lag to get the consecutive times in the same row. But in the case of user Amit that spans across multiple days. Also there could be a user who has started and stopped multiple times on the same day. Even if I do that how could I generate hours for the dates amidst the range. Can you please help me.

This should work. You will only get Hours spent if both Start and Stop status exists for a user in a single day.
SELECT SystemName,[user],
CONVERT(varchar, CAST(Datetime AS DATETIME), 1) Datetime,
DATEDIFF
(
HH,
MAX(CASE WHEN Ststus = 'Start' THEN [Datetime] ELSE NULL END ),
MAX(CASE WHEN Ststus = 'Stop' THEN Datetime ELSE NULL END )
)HourSpent
FROM your_table A
GROUP BY SystemName,[User],
CONVERT(varchar, CAST(Datetime AS DATETIME), 1)

Since the output consists of one row per User + Day, then you would need to JOIN the data to a calendar table of dates.
You would need a way to extract the Start and Stop timestamp pairs for a given user, join it to the calendar table, then count the number of hours on that day that are between the start and stop times. (The hour count could be use a User Defined Function.
That's pretty complex. Frankly, I would rather write a Python program to parse the data rather than doing it via SQL. It would be very simple:
Read start line
Read end line
Loop through days, outputting hours per day (quite simple in Python)
Sometimes the best hammer is a spanner. (Translation: Sometimes a different tool is better.)

Related

SQL Finding the average time between user-event over all users

I'm trying to get an output which I don't know how to formulate.
The Case:
I need the average time elapsed between the 1st and 10th event from the same user, and then an average of that time from all users.
I tried writing it, but i have no clue where to start writing the syntax to get the result i desire.
So, I need:
WHEN Count(user_event = 10) THEN avg(date(1st event and 20th event))
This seems to me to be the logical way. But I'm a novice at sql and I'm trying to get this data. But I can't get anywhere near the output I need.
SELECT
WHEN Count(user_event = 10) THEN avg(date(1st event and 20th event))
from my_table
;
The average time between event 1-10 over all users.
my_table contains:
ID, date, name, user_id

How to get date difference in days in MySql

In mysql, I have a user table with columns
ban_start_date [datetime]
ban_days [int]
Where this means that the user is banned from ban_start_date for another ban_days days more. How can I select the user table, but add a new column saying how many days left they are banned?
I know I need to check if NOW() is in between ban_start_date and ban_start_date+ban_days, and if so, get the difference. Something like that.
Thanks
Please try:
SELECT
DATEDIFF(DATE_ADD(banned_users.ban_start_date , INTERVAL banned_users.ban_days DAY), DATE(CURDATE())) AS ban_days_left
FROM
users as banned_users
WHERE
DATE_ADD(banned_users.ban_start_date , INTERVAL banned_users.ban_days DAY) > DATE(CURDATE());
Query takes only users who are banned at execution time and then adds the ban_days to the ban_start_date temporarily producing ban-enddate (DATE_ADD(banned_users.ban_start_date , INTERVAL banned_users.ban_days DAY)). After that DATEDIFF is used to count the days between ban-enndate and today's date, which in fact should result in ban_days_left.
Why not store the ban_end_date instead? If you want you can store the ban_days, but you should not need that value very often.
The nice thing (I think) about storing the bad_end_date is that people who are not banned will have that as a NULL. It is extremely easy to look up this one value and tell the user:
"You are banned until ban_end."
At the start of the day you will have check for people with non-NULL bad_end_date values where ban_end_date < now() and set those to NULL. Easy.

Aggregating/Grouping a set of rows/records in MySQL

I have a table say "sample" which saves a new record each five minutes.
Users might ask for data collected for a specific sampling interval of say 10 min or 30 min or an hour.
Since I have a record every five minutes, when a user asks for data with a hour sample interval, I will have to club/group every 12 (60/5) records in to one record (already sorted based on the time-stamp), and the criteria could be either min/max/avg/last value.
I was trying to do this in Java once I fetch all the records, and am seeing pretty bad performance as I have to iterate through the collection multiple times, I have read of other alternatives like jAgg and lambdaj, but wanted to check if that's possible in SQL (MySQL) itself.
The sampling interval is dynamic and the aggregation function (min/max/avg/last) too is user provided.
Any pointers ?
You can do this in SQL, but you have to carefully construct the statement. Here is an example by hour for all four aggregations:
select min(datetime) as datetime,
min(val) as minval, max(val) as maxval, avg(val) as avgval,
substring_index(group_concat(val order by datetime desc), ',', 1) as lastval
from table t
group by floor(to_seconds(datetime) / (60*60));

Calculating time difference between activity timestamps in a query

I'm reasonably new to Access and having trouble solving what should be (I hope) a simple problem - think I may be looking at it through Excel goggles.
I have a table named importedData into which I (not so surprisingly) import a log file each day. This log file is from a simple data-logging application on some mining equipment, and essentially it saves a timestamp and status for the point at which the current activity changes to a new activity.
A sample of the data looks like this:
This information is then filtered using a query to define the range I want to see information for, say from 29/11/2013 06:00:00 AM until 29/11/2013 06:00:00 PM
Now the object of this is to take a status entry's timestamp and get the time difference between it and the record on the subsequent row of the query results. As the equipment works for a 12hr shift, I should then be able to build a picture of how much time the equipment spent doing each activity during that shift.
In the above example, the equipment was in status "START_SHIFT" for 00:01:00, in status "DELAY_WAIT_PIT" for 06:08:26 and so-on. I would then build a unique list of the status entries for the period selected, and sum the total time for each status to get my shift summary.
You can use a correlated subquery to fetch the next timestamp for each row.
SELECT
i.status,
i.timestamp,
(
SELECT Min([timestamp])
FROM importedData
WHERE [timestamp] > i.timestamp
) AS next_timestamp
FROM importedData AS i
WHERE i.timestamp BETWEEN #2013-11-29 06:00:00#
AND #2013-11-29 18:00:00#;
Then you can use that query as a subquery in another query where you compute the duration between timestamp and next_timestamp. And then use that entire new query as a subquery in a third where you GROUP BY status and compute the total duration for each status.
Here's my version which I tested in Access 2007 ...
SELECT
sub2.status,
Format(Sum(Nz(sub2.duration,0)), 'hh:nn:ss') AS SumOfduration
FROM
(
SELECT
sub1.status,
(sub1.next_timestamp - sub1.timestamp) AS duration
FROM
(
SELECT
i.status,
i.timestamp,
(
SELECT Min([timestamp])
FROM importedData
WHERE [timestamp] > i.timestamp
) AS next_timestamp
FROM importedData AS i
WHERE i.timestamp BETWEEN #2013-11-29 06:00:00#
AND #2013-11-29 18:00:00#
) AS sub1
) AS sub2
GROUP BY sub2.status;
If you run into trouble or need to modify it, break out the innermost subquery, sub1, and test that by itself. Then do the same for sub2. I suspect you will want to change the WHERE clause to use parameters instead of hard-coded times.
Note the query Format expression would not be appropriate if your durations exceed 24 hours. Here is an Immediate window session which illustrates the problem ...
' duration greater than one day:
? #2013-11-30 02:00# - #2013-11-29 01:00#
1.04166666667152
' this Format() makes the 25 hr. duration appear as 1 hr.:
? Format(#2013-11-30 02:00# - #2013-11-29 01:00#, "hh:nn:ss")
01:00:00
However, if you're dealing exclusively with data from 12 hr. shifts, this should not be a problem. Keep it in mind in case you ever need to analyze data which spans more than 24 hrs.
If subqueries are unfamiliar, see Allen Browne's page: Subquery basics. He discusses correlated subqueries in the section titled Get the value in another record.

Counting hours using MySQL

I'm creating simple project where I count the employee attendance and other things
when employee late on work ,I have to enter the delay time, example
employee came to work late by 30 min, so I will enter on the field 30:00 or 00:30:00 , in the end of each month , week, or day, I want to calculate the time, such as 30 min + 1H + 25 min and in the end of each month I will get the total of all that hours and minutes on the report, the report will show me the average and the sum of that time.
the following fields ( Timing Logged in Duration , Total Talking Time, Work , Time Not ready Time ) I want to calculate the hours and minutes.
what I’m getting in the report is showing the SUM of the hours I entered as time with AM/PM which is not what I’m looking for.
this is MySQL query
SELECT
ID,
`Date`,
`Shift Time`,
`In charge`,
`Agent Name`,
Attendance,
Timing,
`Logged in Duration`,
`Total Talking Time`,
`Work Time`,
`Not ready Time`,
`Calls Handled`,
RNA,
`Shift Calls Presented`,
`Shift Calls Handled`,
`HD Calls Abandoned`,
`Upload Your Report`
FROM `shift report`
I thought I would post you the answer in the perspective of summing up time value releated issues developers, users have face so far. It's not just about formatting.
You may or may not have noticed that there's posibility sum of your total time can be miscalculated by the engine. Further mysql engine could return null although you have values.
When you are adding/aggregating time values it is converted as a number and results will be in number format. If you attempt to adding up time values like the following:
e.g. 01:38:50, 03:40:25 --> should ideally result in 05:19:15
If you do Sum(the above two) --> you get a number
If you use CAST(Sum(the above two) AS TIME) --> you get null
There are two other possibilities showed in the my code snippet.
Sample code reference. It will show you the different results that we just discussed. As for now, it seems like three way formatting could help.
time_format(sec_to_time(sum(time_to_sec(logged_duration))),'%h:%i:%s') total_log_duration
The bug reported to MySQL is not rectified yet.