Sql Request SUM time - php

I have two tables, workers and schedule. The workers every day, they log their start time and end time work. The column total_time have the number of hours worked for every day.
CREATE TABLE workers (
id,
full_name,
....
)
CREATE TABLE schedule (
id,
worker_id,
date,
start_time,
end_time,
total_time
)
What I need I don't know if it's possible is, select the records from the schedule table and join the workers table to get the theirs names for a date range. Usually it's a period of two weeks and them return the worker name with the number of hours worked for the requested dates something like that.
John Doe | 2018-09-03, 2018-09-15 | 80 hrs
Worker 2 | 2018-09-03, 2018-09-15 | 75 hrs
Worker 3 | 2018-09-03, 2018-09-15 | 83.35 hrs
Thank You!!

select w.full_name, '2018-09-28', '2018-10-05'
sum(total_time)
from workers w
join schedule s on s.worker_id = w.id
where start_time >= '2018-09-28' and start_time < '2018-10-05'
group by w.full_name

Use join and aggregation
select workers.id, full_name,DATEDIFF(second, min(start_time), max(end_time)) / 3600.0
from workers inner join shcedule
on workers.id=shcedule.worker_id
group by workers.id,full_name

Related

Sum Time SQlite PHP over 24Hrs

I have a list of times that I need to sum. I have two tables: workers and schedule.
CREATE TABLE schedule (
id,
worker_id,
date,
start_time,
end_time,
hours
SELECT w.name, w.salary, time(sum(strftime('%s', hours) - strftime('%s', '00:00:00')), 'unixepoch') AS total_hours
FROM workers w
JOIN schedule s ON s.worker_id = w.id
WHERE date between '2018-09-17' AND '2018-09-21'
GROUP BY w.name
The column hours contain the total of hours worked for every day. I found some other posts, but not the solution that I need.
sqlite: how to add total time hh:mm:ss where column datatype is DATETIME?
sqlite: sum time over 24h to HHH:MM:SS
Problem
Image schedule table
This request works fine if I sum values under 24Hrs : example 10:00 + 07:00 the total will be : 17:00, but if I sum more times : 10:00 + 07:00 + 09:00 I will get :02:00.
I don't know what I am doing wrong.
The result what I am looking for :
Worker | Salary | Total Hours
John Doe | $28.00 | 26:00
worker 1 | $30.00 | 20:15
worker 2 | $25.00 | 42:30
Just represent the value as decimal hours, so 10:30 would be represented instead at 10.5:
SELECT w.name, w.salary,
sum(strftime('%s', hours) - strftime('%s', '00:00:00')) / (60.0 * 60) as decimal_hours
FROM workers w JOIN
schedule s
ON s.worker_id = w.id
WHERE date between '2018-09-17' AND '2018-09-21'
GROUP BY w.name;
You can format this back into a string of the form HH:MM, if you really, really want. I suggest that you keep it as decimal hours or minutes, though.

Get total hours with PHP & MySQL

I have the following table
id | user_id | date | status
1 | 53 | 2018-09-18 06:59:54 | 1
2 | 62 | 2018-09-18 07:00:16 | 1
3 | 53 | 2018-09-18 09:34:12 | 2
4 | 53 | 2018-09-18 12:16:27 | 1
5 | 53 | 2018-09-18 18:03:19 | 2
6 | 62 | 2018-09-18 18:17:41 | 2
I would like to get the total working hours (from date range) and group them by user_id
UPDATE
The system does not "require" a check-out so if there is only one value can we set a default check out time lets say 19:00:00? IF not I can check every day at 21:00:00 if there is not a checkout time to manually insert it at 19:00:00
UPDATE 2
I have added a new field in the table "status" so the very first check-in of the date the status = 1 and every 2nd check-in the status = 2
So if a user check-ins for the 3rd time during the day the status will be 1 again etc.
I hope this will make things easier
Thanks
In case of multiple check-in and check-out happening within a day, for a user:
Utilizing Correlated Subquery, we can find corresponding "checkout_time" for every "checkin_time".
Also, note the usage of Ifnull(), Timestamp() functions etc, to consider default "checkout_time" as 19:00:00, in case of no corresponding entry.
Then, considering this enhanced data-set as Derived Table, we group the data-set based on the user_id and date. Date (yyyy-mm-dd) can be determined using Date() function.
Eventually, use Timestampdiff() function with Sum aggregation, to determine the total work seconds for a user_id at a particular date.
You can easily convert these total seconds to hours (either in your application code, or at the query itself (divide seconds by 3600).
The reason I have preferred to compute using seconds, as Timestampdiff() function returns integer only. So there may be truncation errors, in case of multiple checkin/checkout(s).
Use the following query (replace your_table with your actual table name):
SELECT inner_nest.user_id,
DATE(inner_nest.checkin_time) AS work_date,
SUM(TIMESTAMPDIFF(SECOND,
inner_nest.checkin_time,
inner_nest.checkout_time)) AS total_work_seconds
FROM
(
SELECT t1.user_id,
t1.date as checkin_time,
t1.status,
IFNULL( (
SELECT t2.date
FROM your_table AS t2
WHERE t2.user_id = t1.user_id
AND t2.status = 2
AND t2.date > t1.date
AND DATE(t2.date) = DATE(t1.date)
ORDER BY t2.date ASC LIMIT 1
),
TIMESTAMP(DATE(t1.date),'19:00:00')
) AS checkout_time
FROM `your_table` AS t1
WHERE t1.status = 1
) AS inner_nest
GROUP BY inner_nest.user_id, DATE(inner_nest.checkin_time)
Additional: Following solution will work for the case when there is a single check-in, and corresponding check-out on the same date.
You first need to group the dataset based on the user_id and date. Date (yyyy-mm-dd) can be determined using Date() function.
Now use aggregation functions like Min() and Max() to find the starting and closing time for a user_id at a particular date.
Eventually, use Timestampdiff() function to determine the working hours for a user_id at a particular date (difference between the closing and starting time)
Try the following query (replace your_table with your actual table name):
SELECT user_id,
DATE(`date`) AS working_date,
TIMESTAMPDIFF(HOUR, MIN(`date`), MAX(`date`)) AS working_hours
FROM your_table
GROUP BY
user_id,
DATE(`date`)
Use TIMESTAMPDIFF function
the query more like :
SELECT t1.user_id, TIMESTAMPDIFF(HOUR,t1.date,t2.date) as difference
FROM your_table t1
INNER JOIN your_table t2 on t1.user_id = t2.user_id
Group By t1.user_id
You can see this as preference TimeStampDiff

MySql query for cohort analysis

I am working with MySql and Symfony2. I need to build cohort analysis table. I need to compare how many users in each cohort log in to website at least once a week after they register. What I tried to do is to get number of registered users by week, basically these are my cohorts.
SELECT DATE_FORMAT(date_added,'%d %b %y') as reg_date, COUNT(*) AS user_count
FROM user
WHERE date_added>='2016-02-01' AND date_added<=NOW()
GROUP BY WEEK(date_added)
This query gets distinct users logged in to website by week.
SELECT WEEK(login_date) AS week, COUNT(DISTINCT user_id) AS user_count
FROM user_log
WHERE login_date>='2016-02-01' AND login_date<=NOW()
GROUP BY WEEK(login_date)
My problem: I can't figure out how to group logged in users by cohorts and compare cohorts by weeks. I hope I stated problem clearly. English is not my first language. Thanks.
Sample data:
user table
id | date_added (in WEEK() format)
A | 1
B | 1
C | 1
D | 2
E | 2
F | 2
G | 2
------------
user_log table
user_id | login_date (in WEEK() format)
A | 1
B | 1
B | 1
A | 2
D | 2
A | 2
D | 2
E | 2
Expected table. Cohort 1 - users registered in week 1, cohort 2- in week etc. Size - number of registered users. Week 1 - how many users logged back to website in a first week after registration, Week 2 - how many users logged back to website in a second week after registration
Cohort size Week1 Week2
Cohort 1 | 3 | 2 | 1 |
Cohort 2 | 4 | 2 | - |
This is borrowed from my modification of #Andriy M's answer of this question: Cohort analysis in SQL
This query gets unique user logins by week after registering.
SELECT DISTINCT
user_id,
FLOOR(DATEDIFF(user_log.login_date, user.date_added)/7) AS Offset
FROM user_log
LEFT JOIN user ON (user.id = user_log.user_id)
WHERE user_log.login_date >= CURDATE() - INTERVAL 14 DAY
This query gets all the users created in the past 14 days and formats the date to the week they signed up:
SELECT
id,
DATE_FORMAT(date_added, "%Y-%u") AS cohort
FROM user
WHERE date_added >= CURDATE() - INTERVAL 14 DAY
We can put those two queries together to get a table with how many people came back after registering:
SELECT STR_TO_DATE(CONCAT(u.cohort, ' Monday'), '%X-%V %W') as date,
SUM(s.Offset = 0) AS size,
SUM(s.Offset = 1) AS Week1,
SUM(s.Offset = 2) AS Week2
FROM (
SELECT
id,
DATE_FORMAT(date_added, "%Y-%u") AS cohort
FROM user
WHERE date_added >= CURDATE() - INTERVAL 21 DAY
) as u
LEFT JOIN (
SELECT DISTINCT
user_id,
FLOOR(DATEDIFF(user_log.login_date, user.date_added)/7) AS Offset
FROM user_log
LEFT JOIN user ON (user.id = user_log.user_id)
WHERE user_log.login_date >= CURDATE() - INTERVAL 21 DAY
) as s
ON s.user_id = u.id
GROUP BY u.cohort
ORDER BY u.cohort
Since we aren't counting how many people registered in a given week, we are assuming that they logged at lease once in the week they registered to give an accurate result for the size column.
Also you'll have to rework this to get a number for the cohort instead of the date, but I find dates more helpful.
Also you can extend this to more weeks - you'll have to change the number of days after INTERVAL in both subqueries, and you can add more rows on in the main select statement to get more weeks.

Query to get vacant time MYSQL [duplicate]

This question already has answers here:
mysql query room availability
(2 answers)
Closed 7 years ago.
Would like some logical help on formulating a MYSQL Query that gets results that isn't within the data of the table.
I have a table named schedule that has columns with data type 'time' that indicates when this certain schedule starts and ends and a foreign key referencing from table 'rooms' in which the schedule will take place. And in the php code in its search feature, I wanted to add a feature that shows results of rooms that are currently not being occupied by a schedule or is vacant. I added a jquery slider to specifically fetch the start time and end time the searcher wanted.
TABLE 'schedule'
room sched_start sched_end
1 09:00:00 10:00:00
1 11:00:00 12:00:00
2 07:30:00 08:30:00
2 11:30:00 13:00:00
For example, the searcher wanted to search a vacant room from 10:00:00 to 11:00:00. Basing from the database, the result should show that both rooms, room 1 and room 2, should be displayed in the search result as both rooms won't be occupied within the specified time of the searcher. I was thinking of comparing chronologically the schedule of all the similar rooms, the 'sched_end' of the first row or the first schedule and the sched_start of the succeeding row or the schedule and so on, so to determine whether there is a vacant time in between. Can anyone help me on this?
All helps and hates would be very much appreciated as I can be as much noob in MySQL-ing.
DROP TABLE IF EXISTS schedule;
CREATE TABLE schedule
(room INT NOT NULL
,schedule_start TIME NOT NULL
,schedule_end TIME NOT NULL
,PRIMARY KEY(room,schedule_start)
);
INSERT INTO schedule VALUES
(1,'09:00:00','10:00:00'),
(1,'11:00:00','12:00:00'),
(2,'07:30:00','08:30:00'),
(2,'11:30:00','13:00:00'),
(3,'09:30:00','10:30:00'),
(3,'11:00:00','12:00:00'),
(4,'10:30:00','10:45:00');
SET #start:= '10:00:00';
SET #end:= '11:00:00';
SELECT DISTINCT x.room
-- or whatever columns you want from whichever table you want
FROM schedule x
LEFT
JOIN schedule y
ON y.room = x.room
AND y.schedule_start < #end
AND y.schedule_end > #start
-- other tables can join in here
WHERE y.room IS NULL;
+------+
| room |
+------+
| 1 |
| 2 |
+------+
http://sqlfiddle.com/#!9/1b677/1
Just to demonstrate that #M0rtiis's solution is wrong...
SELECT DISTINCT room
FROM schedule
WHERE #end <= schedule_start
OR #start >= schedule_end;
+------+
| room |
+------+
| 1 |
| 2 |
| 3 |
+------+
What you need is to specifically exclude the rooms that are occupied in the given period.
SET #start = '10:00:01';
SET #end = '10:59:59';
SELECT *
FROM `schedule` -- you probably want to select from rooms here...
WHERE room NOT IN (
SELECT room
FROM `schedule`
WHERE sched_start BETWEEN #start AND #end
OR sched_end BETWEEN #start AND #end
OR #start BETWEEN sched_start AND sched_end
OR #end BETWEEN sched_start AND sched_end
);
Note that I compensated the "start inclusive" behaviour by adding one second to the start time and subtracting one second from the end time. You should do that before you feed the times to SQL, to avoid those calculations there.
This query filters all cases, including overlapping meetings.
Or, perhaps slightly more coherently:
SET #start:= '10:00:00';
SET #end:= '11:00:00';
SELECT DISTINCT room
FROM schedule
WHERE room NOT IN ( SELECT room
FROM schedule
WHERE schedule_start < #end
AND schedule_end > #start );
Also, you really need proper indexes if this query is to perform with more than just a couple of rows. Use the EXPLAIN function to help you.
Its bad idea to store there TIME. use DATETIME instead to cover cases where need_start - one day and need_end - another (next? or i want to be guest in your hotel for a week?) day.
But anyway, on what u have now try this
SELECT DISTINCT
room
FROM schedule
WHERE
'11:00:00' <= sched_start
OR
'10:00:00' >= sched_end
http://sqlfiddle.com/#!9/dafae/9
You can use BETWEEN operator.
SELECT *
FROM schedule
WHERE sched_end BETWEEN '10:00:00' AND '11:00:00'

SQL query to count number of days with reappearing entries

I have a database with access controll log entries:
time : datetime (this is the access timestamp)
src: text (this is the userid)
I want to get a list out of it that shows how many users from the current day had already access on how many days during the past 7 days. The result should look like this:
number of days with access | count
1 | 30
2 | 54
3 | 123
4 | 843
5 | 3490
6 | 71
7 | 23
What I have so far:
The query below returns the number of users with log entry on 2015-03-08 that had also an entry on 2015-03-07.
SELECT Count(DISTINCT a.src)
FROM contacts AS a
LEFT JOIN contacts AS b
ON a.src = b.src
WHERE a.time BETWEEN Cast('2015-03-08 05:00:00' AS DATETIME) AND Cast('2015-03-09 05:00:00' AS DATETIME)
AND b.time BETWEEN Cast('2015-03-07 05:00:00' AS DATETIME) AND Cast('2015-03-08 05:00:00' AS DATETIME)
But I'm stuck with getting the count for each dayby number of days as described above. If there is no 'sql only' solution it would be ok as well to have an (performant) approach using php. Thanks for any help..
I don't see any reason why do you need to join b table.
SELECT
DAY(a.time),
COUNT(DISTINCT a.src)
FROM contacts AS a
WHERE a.time
BETWEEN (TIMESTAMP(CURDATE()) - INTERVAL 1 WEEK)
AND TIMESTAMP(CONCAT(CURDATE(),' 23:59:59'))
GROUP BY DAY(a.time)

Categories