Select distinct and get sum of timestamp differences - mysql

I don't know if this is possible, but it'd be really awesome. I have a table of sign-ins for people who are logging time on different projects and I need to compile a report of time logged for each project for a given time period.
My table looks something like this:
id | project | time_in | time_out | break
----------------------------------------------------------------
1 | 1 | 2014-12-07 05:00:00 | 2014-12-07 10:00:00 | 30
2 | 2 | 2014-12-07 06:00:00 | 2014-12-07 13:00:00 | 15
3 | 1 | 2014-12-07 14:00:00 | 2014-12-07 18:00:00 | 0
4 | 3 | 2014-12-07 08:30:00 | 2014-12-07 18:45:00 | 75
5 | 2 | 2014-12-07 12:00:00 | 2014-12-07 16:30:00 | 0
What I'd like to be able to do is get a report of the time logged for each project given a date range, i.e. the total time, probably in seconds, logged for each project.
time_in and time_out are fields of type TIMESTAMP; break is an integer representing the number of minutes the person was on break. I need to get the sum of time_out - time_in - break for each project, e.g. for December 7:
project | time
---------------
1 | 34200
2 | 40500
3 | 34200
This is all I have so far:
SELECT DISTINCT
`project`
FROM `sign_ins`
WHERE
`time_in` >= '2014-12-07 00:00:00' AND
`time_out` <= '2014-12-08 00:00:00';
I appreciate your help on this, SO community. You guys are so brilliant.

You can get the difference in seconds by converting the date/time values to Unix time stamps. Then, just aggregate the differences using sum():
SELECT project,
SUM(UNIX_TIMESTAMP(time_out) - UNIX_TIMESTAMP(time_in) - (break * 60)) as DiffSecs
FROM `sign_ins`
WHERE `time_in` >= '2014-12-07 00:00:00' AND
`time_out` <= '2014-12-08 00:00:00'
GROUP BY project;

Related

Mysql get weekly average of daily data

I have a table called primeWeek. Im trying to get weekly avg depends on dates.
Example of my table
id | count | date
1 | 70 | 2020-08-29
2 | 67 | 2020-08-30
3 | 69 | 2020-08-31
4 | 82 | 2020-09-01
5 | 73 | 2020-09-02
I tried few things but results are not correct.
count and date are both keywords in SQL, so you should surround them with backticks.
SELECT
AVG(`count`) AS primeCount,
CONCAT(`date`, '-', `date` + INTERVAL 6 DAY) AS week
FROM primeWeek
GROUP BY WEEK(`date`)
ORDER BY WEEK(`date`);

get total working hour based of day of the week

I want to check if employee's daily working hour is less than total working hour assigned.
For example employee #1 are assigned to work :
Day | Total working hours
mon | 10
tue | 10
wed | 10
thu | 10
fri | 10
sat | 0
sun | 0
I have total working hour of employee #1 as below :
Start | End | Total Hours Worked
2018-07-02 00:28:29 | 2018-07-02 04:12:17 | 3.72
2018-07-05 00:26:20 | 2018-07-05 05:03:23 | 4.62
2018-07-12 00:27:35 | 2018-07-12 10:21:08 | 9.88
The problem I'm facing is to know which day of the week to check the total working hour based on date. e.g. 2018-07-02 is monday so the total working hour employee should work is 10 hours however the total hour worked by the employee is only 3.72. Hence for 2018-07-02 employee has not fullfill total working hour assigned.
The expected query result should return :
Short Working Hour Date
2018-07-02
2018-07-05
2018-07-12
Appreciate any help from you guys. Thanks 🙏🏻
I found a way to do it refering the manual. But it looks messy.
SELECT
start,
end,
total_hours_worked
FROM mytable
GROUP BY DATE(start), DATE(end)
having CASE
WHEN DAYOFWEEK(MIN(start))=1 and total_hours_worked < 0 THEN 1
WHEN DAYOFWEEK(MIN(start))=2 and total_hours_worked < 10 THEN 1
WHEN DAYOFWEEK(MIN(start))=3 and total_hours_worked < 10 THEN 1
WHEN DAYOFWEEK(MIN(start))=4 and total_hours_worked < 10 THEN 1
WHEN DAYOFWEEK(MIN(start))=5 and total_hours_worked < 10 THEN 1
WHEN DAYOFWEEK(MIN(start))=6 and total_hours_worked < 10 THEN 1
WHEN DAYOFWEEK(MIN(start))=7 and total_hours_worked < 0 THEN 1
ELSE 0
END;
I don't understand exactly what you're after, so here's an incomplete solution...
DROP TABLE employee_assignments;
CREATE TABLE employee_assignments
(day CHAR(3) NOT NULL PRIMARY KEY
,total_working_hours INT NOT NULL DEFAULT 0
);
INSERT INTO employee_assignments VALUES
('mon',10),
('tue',10),
('wed',10),
('thu',10),
('fri',10),
('sat',0),
('sun',0);
DROP TABLE timesheet;
CREATE TABLE timesheet
(start DATETIME NOT NULL PRIMARY KEY
,end DATETIME NOT NULL
);
INSERT INTO timesheet VALUES
('2018-07-02 00:28:29','2018-07-02 04:12:17'),
('2018-07-05 00:26:20','2018-07-05 05:03:23'),
('2018-07-12 00:27:35','2018-07-12 10:21:08');
SELECT t.*
, (TIME_TO_SEC(t.end)-TIME_TO_SEC(t.start))/3600 n
, a.*
FROM timesheet t
JOIN employee_assignments a
ON a.day = DATE_FORMAT(t.start,'%a');
+---------------------+---------------------+--------+-----+---------------------+
| start | end | n | day | total_working_hours |
+---------------------+---------------------+--------+-----+---------------------+
| 2018-07-02 00:28:29 | 2018-07-02 04:12:17 | 3.7300 | mon | 10 |
| 2018-07-05 00:26:20 | 2018-07-05 05:03:23 | 4.6175 | thu | 10 |
| 2018-07-12 00:27:35 | 2018-07-12 10:21:08 | 9.8925 | thu | 10 |
+---------------------+---------------------+--------+-----+---------------------+

Mysql - Rows within hourly ranges on specific days using DAYOFWEEK

I am trying to get a query that will return a result set with reading total within certain hour ranges (defined in the working_hours table) depending on the DAYOFTHEWEEK for the date with a result that looks like:
working | nonworking | weekend | date | group_id
-----------------------------------------------------------------
50.3 | 30.8 | 0 | 2015-04-01 00:00 | 7
40.3 | 60.8 | 0 | 2015-04-01 00:00 | 8
50.3 | 30.8 | 0 | 2015-04-02 00:00 | 7
40.3 | 60.8 | 0 | 2015-04-02 00:00 | 8
Working and Weekend ranges are stored in the database in working_hours, Nonworking time ranges are implied (NOT BETWEEN the other ranges on that day basically)
The tables are as following:
Readings table has the hourly readings, named readings
group_id | reading | datestamp
------------------------------------------------------
7 | 30.8 | 2015-04-01 00:00
7 | 20.2 | 2015-04-01 01:00
7 | 11.2 | 2015-04-02 00:00
7 | 20.2 | 2015-04-02 01:00
8 | 26.2 | 2015-04-01 00:00
8 | 30.2 | 2015-04-01 01:00
8 | 26.2 | 2015-04-02 00:00
8 | 30.2 | 2015-04-02 01:00
Hour Ranges are stored in the working_hours table, the day column is DAYOFTHEWEEK format (1 = Sunday, 2 = Monday, etc):
group_id | day | range_start | range_end | range_type_id | day_type_id
------------------------------------------------------------------------------
7 | 5 | 08:00:00 | 15:59:00 | 1 | 1
7 | 6 | 00:00:00 | 05:59:00 | 1 | 2
7 | 6 | 06:00:00 | 23:59:00 | 2 | 2
7 | 1 | 00:00:00 | 22:59:00 | 2 | 4
7 | 1 | 23:00:00 | 23:59:00 | 1 | 4
Day Types are in the working_hours_day_type table and where things get complicated for me, Weekday and Weekend only have one range but Start/End Weekend have two ranges ('Start Weekend' first range is working hours, second range weekend hours and 'End Weekend' first range is weekend hours, second range working hours).
id | type
------------------
1 | Weekday
2 | Start Weekend
3 | Weekend
4 | End Weekend
Range Types are in the working_hours_range_type table:
id | type
------------------
1 | Working
2 | Weekend
My Mysql knowledge is limited to simple SELECT, INSERT etc and the basics of JOINs - I have found out about HOUR(datestamp) BETWEEN 8 AND 14 but dont know how to get subqueries to iterate within a parent query using WHERE datestamp BETWEEN '2015-04-01 00:00:00' AND '2015-04-02 23:59:00' if in fact thats how its done...
I am not totally clear on what constitutes working hours or non-working hours, but does this work?
SELECT
sum(CASE WHEN rtype.range_type_id = 1 THEN reading ELSE 0 END) AS working
sum(CASE WHEN rtype.range_type_id = 2 THEN reading ELSE 0 END) AS nonworking
CASE WHEN r1.daynum in (7,1) THEN 1 ELSE 0 END as weekend
date(datestamp) as date
r1.group_id
FROM
(SELECT
group_id,
reading,
time(datestamp) as rTime,
case when weekday(datestamp) = 0 THEN 2 #weekday() monday to working hours monday
when weekday(datestamp) = 1 THEN 3
when weekday(datestamp) = 2 THEN 4
when weekday(datestamp) = 3 THEN 5
when weekday(datestamp) = 4 THEN 6
when weekday(datestamp) = 5 THEN 7
when weekday(datestamp) = 6 THEN 1
else NULL
END CASE AS daynum
FROM readings) AS r1
LEFT JOIN working_hours w1
ON (r1.daynum = w1.day)
AND (r1.group_id = w1.group_id)
AND (r1.rTime BETWEEN w1.range_start AND w1.range_end)
LEFT JOIN working_hours_day_type dtype
ON w1.day_type_id = dtype.id
LEFT JOIN working_hours_range_type rtype
ON w1.range_type_id = rtype.id
GROUP BY
CASE WHEN daynum in (7,1) THEN 1 ELSE 0 END,
date(datestamp) as date,
r1.group_id

mysql Return amount of order for hour with on colum day of month

I have a mysql db which I use to return amounts of orders by hour in a specific day. I use this SELECT statement for that.
select
hour(datains),sum(valore)
from
ordini
where (stato=10 or stato = 1 ) and DATE(datains) = DATE_SUB(CONCAT(CURDATE(), ' 00:00:00'), INTERVAL 0 DAY)
group by hour(datains)
order by
id DESC
It returns:
+--------------+---------------+
| hour datains | valore |
| 12 | 34 |
| 11 | 56 |
| 10 | 134 |
+-------------------------------
Now I need to have columns for a certain number of days, like this.
+--------------+---------------+--------------+--------------+
| hour datains | 01-01-2014 | 02-01-2014 | 03-01-2014 |
| 12 | 34 | 34 | 77 |
| 11 | 56 | 0 | 128 |
| 10 | 134 | 66 | 12 |
+------------------------------+-----------------------------+
Is this possible?
It seems you have a table ordini with columns datains, valore, and stato.
Perhaps you can try this query to generate hour-by-hour aggregates for a three days' worth of recent sales, but not including today.
SELECT DATE_FORMAT(datains, '%Y-%m-%d %H:00') AS hour,
SUM(valore) AS valore
FROM ordini
WHERE (stato = 1 OR stato = 10)
AND datains >= CURRENT_DATE() - INTERVAL 3 DAY
AND datains < CURRENT_DATE
GROUP BY DATE_FORMAT(datains, '%Y-%m-%d %H:00')
ORDER BY DATE_FORMAT(datains, '%Y-%m-%d %H:00')
This will give you a result set with one row for each hour of the three days, for example:
2014-01-01 10:00 456
2014-01-01 11:00 123
2014-01-02 10:00 28
2014-01-02 11:00 350
2014-01-02 12:00 100
2014-01-02 13:00 17
2014-01-03 10:00 321
2014-01-03 11:00 432
2014-01-03 12:00 88
2014-01-03 13:00 12
That's the data summary you have requested, but formatted row-by-row. Your next step is to figure out an appropriate technique to pivot that result set, formatting it so some rows become columns.
It happens that I have just written a post on this very topic. It is here:
http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/

Group By sets of values

for a report I'm trying to query events from different shifts. The shifts start on 6 am, 2 pm, and 10 pm every day, and all of the data in the table is tagged with a datetime timestamp. Previously the graveyard shift wasn't doing anything important, so a simple group by DATE(stamp) was sufficient, but now it's 24/7 and I need to break it down into shifts.
Can anyone explain to me how to use a single group by clause to combine datetime values from a range or a set of values? The difficulty is that each graveyard shift spans two calendar days.
I've considered populating a table with 24 hours and shift numbers, then outer joining it and group by DATE(stamp), HOUR(stamp), but that seems hackish and possibly not even working, plus it would give 24 values for each day instead of 3, which then have to be combined in a superquery or script.
MySQL-specific is perfectly ok, that's all we ever use in the reporting.
Since they are all 8-hour shifts, offset by 6 hours from starting at midnight, you turn Stamp into the start-of-shift time like this:
select
stamp,
adddate(date(subdate(stamp, interval 6 hour)),
interval ((hour(subdate(stamp, interval 6 hour))
div 8) * 8) + 6 hour) as shift_start
from mytable;
This substracts 6 hours, then rounds the hour down to either 0 1 or 2 by using integer division, then expands it out again.
Here's the test code with some edge cases:
create table mytable (stamp datetime);
insert into mytable values ('2011-08-17 22:00:00'), ('2011-08-17 23:01:00'),
('2011-08-18 00:02:00'), ('2011-08-18 05:59:00'), ('2011-08-18 06:00:00'),
('2011-08-18 13:59:00'), ('2011-08-18 14:00:00'), ('2011-08-18 17:59:00');
Output of above query:
+---------------------+---------------------+
| stamp | shift_start |
+---------------------+---------------------+
| 2011-08-17 22:00:00 | 2011-08-17 22:00:00 |
| 2011-08-17 23:01:00 | 2011-08-17 22:00:00 |
| 2011-08-18 00:02:00 | 2011-08-17 22:00:00 |
| 2011-08-18 05:59:00 | 2011-08-17 22:00:00 |
| 2011-08-18 06:00:00 | 2011-08-18 06:00:00 |
| 2011-08-18 13:59:00 | 2011-08-18 06:00:00 |
| 2011-08-18 14:00:00 | 2011-08-18 14:00:00 |
| 2011-08-18 17:59:00 | 2011-08-18 14:00:00 |
+---------------------+---------------------+
Try this:
GROUP BY DATE(DATE_ADD(Stamp,INTERVAL -6 HOUR))
That should keep all your shifts on the same day
I think you should pursue your "table with hours and shift numbers" approach. Further, I think you should consider using a calendar table i.e. a table not just covering 24 hours but the whole past, present and future of your enterprise's expected needs. This is not hackish: rather, it is a tried and tested approach. The idea is that SQL is a declarative language designed to query data in tables so a declarative, data-driven solutions make a lot of sense.