I have a table of scheduled times for an event and a second of actual times the event happened, for example:
Table A
ID Date Scheduled
1 2014-09-01 07:05:00
2 2014-09-02 07:05:00
3 2014-09-03 08:05:00
4 2014-09-04 07:10:00
Table B
ID Date Actual
1 2014-09-01 07:10:00
2 2014-09-02 07:16:00
3 2014-09-03 08:00:00
4 2014-09-04 14:15:00
If we assume that anything within 10 minutes of schedule is considered 'on time', is there a way to return the 'on time performance' using MySQL? In the data set above, the on time performance would be 50%, since two of the events happened within 10 minutes of the schedule.
Supplementary edit: If an event is early, that would also be considered on time
Yes. However, I don't understand why the date and time are in different columns. You just need to join the two tables together and do some conditional logic:
select avg(case when a.date = b.date and a.actual <= a.scheduled + interval 10 minute then 1
when b.date < a.date then 1
else 0
end) as OnTimePerformance
from tablea a join
tableb b
on a.id = b.id;
This doesn't handle the case where an event is scheduled on one day (say 11:55 p.m.) and the actual time is the next day (12:01 a.m.). Your data suggests this does not happen. This condition would be easier if the date and time were in a single column.
Here is another way of doing it
select
round((on_time/tot)*100) as performance
from
(
select
count(*) as tot,
sum(
case when
timestampdiff(minute,concat(t1.Date,' ',t1.Scheduled),concat(t2.Date,' ',t2.Actual)) < 10
then 1
end
) as on_time
from tableA t1
join tableB t2 on t1.id = t2.id
)p;
DEMO
Related
I have two tables, both with a Time column as timestamp type which is filled by default when the row is created: Table1 is updated approximately every 10 seconds:
Time | Val_1a | Val_2a | Val_3a
2021-11-06 13:59:53 | 15 | 10 | 35
2021-11-06 14:00:02 | 12 | 15 | 34
.................
2021-11-06 14:05:25 | 11 | 13 | 35
2021-11-06 14:05:35 | 11 | 17 | 36
Table2 is updated every hour after mathematical operations on table1:
Time | Var_1b | Var_2b | Var_3b
2021-11-06 11:00:00 | 2 | 15 | 30
2021-11-06 12:00:00 | 8 | 12 | 32
2021-11-06 13:00:00 | 12 | 11 | 35
What I would like to get but I'm not able to do in any way, is:
Check that the last table1.Val_2a value is greater than the first table1.Val_2a value written at the beginning of the current hour (with the tables above, check if 17 > 15). If this condition is not met, the entire query must return 0 otherwise:
2a) If the last row in table2 refers to the previous day, then the query result is simply the difference of the two table1.Val_2a values (17 - 15 = 2)
2b) Otherwise their difference is calculated as at point 2a (17-15 = 2) and it is added to the table2.Var_1b value (2 + 12 = 14)
I hope I was able to explain it in a clearly way, and that it all is possible with a single query. Thanks everyone for the support
Sorry, if I add an Answer but I couldn't add the image into the comment.
This is the qwery I used to test the CASE clause
SELECT t1.dtm, t1.Val_2a2, t1.Val_2a1,
CASE WHEN Val_2a2 > Val_2a1
THEN Val_2a2-Val_2a1 ELSE 0 END AS ValF FROM (SELECT DATE_FORMAT(time, '%Y-%m-%d %H:00:00') dtm,
SUBSTRING_INDEX(GROUP_CONCAT(Val_2a ORDER BY time),',',1) Val_2a1,
SUBSTRING_INDEX(GROUP_CONCAT(Val_2a ORDER BY time DESC),',',1) Val_2a2 FROM table1 GROUP BY dtm) t1
and this is the unexpected result
Qwery result
It is possible in a single query but different people will have different method of doing it. Whatever the method is, I personally think that the most important part is to keep the logic intact. The details you've provided in your question got me assuming that this might be a kind of query you're looking for:
SELECT t1.dtm, t1.Val_2a2, t1.Val_2a1, t2.Val_1b2,
CASE WHEN Val_2a2 > Val_2a1
THEN Val_2a2-Val_2a1+Val_1b2 ELSE 0 END AS ValF
FROM
(SELECT DATE_FORMAT(time, '%Y-%m-%d %H:00:00') dtm,
SUBSTRING_INDEX(GROUP_CONCAT(Val_2a ORDER BY time),',',1) Val_2a1 ,
SUBSTRING_INDEX(GROUP_CONCAT(Val_2a ORDER BY time DESC),',',1) Val_2a2
FROM table1
GROUP BY dtm) t1
LEFT JOIN
(SELECT DATE(time) dtm,
SUBSTRING_INDEX(GROUP_CONCAT(Val_1b ORDER BY time DESC),',',1) Val_1b2
FROM table2
GROUP BY dtm) t2
ON DATE(t1.dtm)=t2.dtm;
Demo fiddle
hoping it can help someone else, after some more test this is the final qwery I got, considering I just need a value on the fly without needing of storing it.
Of course every consideration by the experts is more than appreciate.
Thanks to all
SELECT
CASE WHEN
(ABS(t1.Val_2a2) - ABS(t1.Val_2a1)) BETWEEN 0 AND 30
THEN t1.Val_2a2-t1.Val_2a1+t2.Val_1b2
ELSE t2.Val_1b2
END AS My_result
FROM
(SELECT DATE_FORMAT(Time, '%Y-%m-%d %H:00:00') dtm,
(SELECT Val_2a FROM table1 WHERE Time >= DATE_FORMAT(NOW(),"%Y-%m-%d %H:00:00") ORDER BY Time LIMIT 1) Val_2a1,
(SELECT Val_2a FROM table1 WHERE Time >= DATE_FORMAT(NOW(),"%Y-%m-%d %H:00:00") ORDER BY Time DESC LIMIT 1) Val_2a2
FROM table1
GROUP BY dtm
ORDER BY Time DESC LIMIT 1) t1
LEFT JOIN
(SELECT (Time) dtm,
(Val_1b) Val_1b2
FROM table2
GROUP BY dtm ORDER BY dtm DESC LIMIT 1) t2
ON DATE(t1.dtm)= DATE(t2.dtm)
We have a room where we can only have XX number of people inside due to current limitations. They come at different times and stay for a different length of time.
I'm trying to get a sum of people currently inside for each 15-min period for a specific date. The server is MySQL 8.0.21 deployed on AWS RDS.
MySQL 8.0 Table: Booking
ID
Name
PartySize
Date
BookedFrom
BookedTo
1
John
2
2021-01-01
2021-01-01 08:30:00
2021-01-01 10:00:00
2
Mary
4
2021-01-01
2021-01-01 09:00:00
2021-01-01 11:00:00
3
Nick
3
2021-01-01
2021-01-01 10:30:00
2021-01-01 12:30:00
I also have a "helper table" with a time slot for each 24 hour 15-min slot
MySQL Table: Timeslot
ID
Time
1
00:00:00
2
00:15:00
3
00:30:00
35
08:30:00
37
09:00:00
38
09:15:00
For example, when I run this query below, I will get the correct count (6 people) for 09:30. What is the most efficient way to get this result for each 15-min slot? Please note that while the BookedTo (datetime field) value may be past midnight, I will always be only making date specific queries.
SELECT
t.id, b.date, t.time, SUM(b.partysize) AS total
FROM
booking b,
timeslot t
WHERE
b.date = '2021-01-01'
AND t.time = '09:15:00'
AND b.bookedfrom <= '2021-01-01 09:15:00'
AND b.bookedto >= '2021-01-01 09:15:00'
Looking for this output for all times (including zeros)
Slot_ID
Date
Time
Total
33
2021-01-01
08:00:00
0
34
2021-01-01
08:15:00
0
35
2021-01-01
08:30:00
2
36
2021-01-01
08:30:00
2
37
2021-01-01
09:00:00
6
38
2021-01-01
09:15:00
6
SELECT
t.id as slot_id,
coalesce(b.date, '2021-01-01') as date,
t.time,
coalesce(sum(b.partysize),0) as total
FROM
timeslot t
LEFT JOIN booking b
ON t.time >= TIME(b.bookedfrom) AND t.time < TIME(b.bookedto) AND b.date = '2021-01-01'
WHERE
t.time BETWEEN '08:00:00' AND '17:00:00'
GROUP BY
t.id,
b.date,
t.time
Now, you have some confusing other requirements, but basically this works because multiple rows of timeslot will match to a single row of booking because of the time range expressed.
The confusing requirements are, you say it's only for 8-5pm, but "bookings might extend to the next day".. does it mean that a booking will start at 4pm and finish at 9am the next day? in which case you might need to adjust the AND b.date = '2021-01-01' to be more like AND (DATE(b.bookedfrom) = '2021-01-01' OR DATE(b.bookedto) = '2021-01-01') ...
Use a CTE that returns the specific date for which you want the results, which may not be the same as the column Date in Booking and CROSS join it to Timeslot.
The result should be LEFT joined to Booking and then aggregate:
WITH cte(Date) AS (SELECT '2021-01-01')
SELECT t.ID, t.time, c.Date,
COALESCE(SUM(b.PartySize), 0) Total
FROM cte c CROSS JOIN Timeslot t
LEFT JOIN Booking b
ON b.BookedFrom <= CONCAT(c.Date, ' ', t.time)
AND b.BookedTo >= CONCAT(c.Date, ' ', ADDTIME(t.time, '00:15:00'))
WHERE t.time BETWEEN '08:00:00' AND '17:00:00'
GROUP BY t.ID, c.Date, t.time
Since BookedFrom and BookedTo may not contain the same date, it is not safe to compare only the time parts of the 2 columns to the column time of Timeslot.
This is why all these conditions in the ON clause are needed.
See the demo.
this query works great ... if you wanna have all dates for all slots .. you will have to have a date table too (ideally within timeslot -> cross join dates and timeslots) ...
use inner join if you wanna get only matching dates and timeslots ..
SELECT t.id as slot_id
, b.date
, t.time as slot
, sum(ifnull(party_size,0)) as total
FROM test.timeslot t
LEFT JOIN test.booking b
ON t.time BETWEEN time(b.booked_from) AND time(b.booked_to)
GROUP BY t.id
, b.date
, t.time;
for all timeslots and selected dates:
https://www.db-fiddle.com/f/gLt2Fs8HTDUakMahZHxcTi/0
for matching timeslots and dates:
SELECT t.id as slot_id
, b.date
, t.time as slot
, sum(ifnull(party_size,0)) as total
FROM test.timeslot t
JOIN test.booking b
ON t.time BETWEEN time(b.booked_from) AND time(b.booked_to)
GROUP BY t.id
, b.date
, t.time;
I have a MySQL table with a datetime row. How can I find all groups with at least 5 entries within 10 minutes?
My only idea is to write a program (in whatever language) and loop over the timestamps, check always 5 (..) successive entries, calculate the time span between the last and the first and check whether it is below the limit.
Can this be done using a single SQL query too?
(The scenario is is simplified and the numbers are just examples.)
As requested, here comes an example:
id | timestamp | other_column
---|---------------------|-------------
3 | 2017-01-01 11:00:00 | thank
2 | 2017-01-01 11:01:00 | you
1 | 2017-01-01 11:02:00 | for
* 6 | 2017-01-01 11:20:00 | your
* 5 | 2017-01-01 11:21:00 | efforts
* 4 | 2017-01-01 11:22:00 | to
* 7 | 2017-01-01 11:23:00 | help
* 8 | 2017-01-01 11:24:00 | me
9 | 2017-01-01 11:40:00 | :
10 | 2017-01-01 11:41:00 | )
If the count limit is 5 and the timespan limit is 10 minutes, I'd like to get the entries marked with "*". The "id" column is the primary key of the table, but the order is not always the order of the timestamps. The "other_column" is used for a where clause. The table has about 1 million entries.
Try to break this down logically. Sorry for the psuedo code bits, I'm a little short on time.
select t1.id, t1.timestamp, t2.timestamp
from yourtable t1
inner join yourtable t2 on t2.timestamp >= t1.timestamp and t2.timestamp < (t1.timestamp + 20 minutes)
(plus 20 minutes won't work as is, use appropriate add function)
So this will give you a relatively giant list of all ID's joined to any other id's within a 20 minute time interval (including one row for itself). (add, I'm only picking out the first row of the group at this point, easier just to grab the 'header row' here by this timestamp plus 20 minutes and worry about the rest in the next step) If we group by the ID and time, we get a count of how many rows were within 20 minutes:
select id, t1.timestamp, count(1)
from yourtable t1
inner join yourtable t2 on t2.timestamp >= t1.timestamp and t2.timestamp < (t1.timestamp + 20 minutes)
group by id, t1.timestamp
having count(1) > 4
This will now give you a list of all the ID's and it's timestamp that has itself and 4 other records or more within 20 minutes away from that timestamp. Now it depends on how you want to group from here, if you want each of the 5 lines, we can call the query above a subquery and join it back to the main table to get the rows you want returned.
select t3.*
from
(select id, t1.timestamp, count(1)
from yourtable t1
inner join yourtable t2
on t2.timestamp >= t1.timestamp and t2.timestamp < (t1.timestamp + 20 minutes)
group by id, t1.timestamp
having count(1) > 4) a
inner join yourtable t3 on t3.timestamp >= a.timestamp and t3.timestamp < (a.timestamp + 20 minutes)
And that should give you ID 4-8 and it's info returned (order as you see fit).
My apologies that I don't have the time to test, but the logic should work.
I have a table of dates, each date represent a task, the task takes three days to complete.
I want to select all the unbooked dates that doesn't intersects with another booked task.
I've been trying and googling for three days now and I think it is time to ask for help.
date booked
=========== =======
2014-09-01 0
2014-09-02 1
2014-09-05 0
2014-09-10 1
2014-09-15 0
2014-09-16 0
2014-09-20 1
2014-09-25 0
The expected result:
date booked
=========== =======
2014-09-01 0
2014-09-15 0
2014-09-16 0
2014-09-25 0
You can use a left join (by adding 3 days in date column) with is null on same table to get the unbooked dates and doesnot intersects with other task's booked date
select t.*,
t1.date date1
from t
left join t t1 on(t.date = t1.date + interval 3 day)
where t.booked = 0 and t1.date is null
Fiddle Demo
Refer to the following answer.
Detect overlapping date ranges from the same table
If you could change date to Start_Date and add column End_Date (Start_Date + 3), then adding NOT to the answer quoted will result you in an answer that do not have overlapping date ranges.
select dr1.* from date_ranges dr1
inner join date_ranges dr2
where NOT (dr2.start > dr1.start -- start after dr1 is started
and dr2.start < dr1.end) -- start before dr1 is finished
From the results of the above query you can select the row which have 0 for booked column.
For some odd reason, group by week is returning odd date intervals with a datetime field.
"Completed" is a datetime field, and using this query:
SELECT
Completed,
COUNT( DISTINCT Table1.ID ) AS ActivityCount
FROM Table1
JOIN Table1Items
ON Table1.ID = Table1Items.ID
JOIN database_database.Table2
ON Table2.Item = Table1Items.Item
WHERE Completed != '0000-00-00' AND Completed >= '2012-09-25' AND Completed <= '2012-10-25'
GROUP BY WEEK(Completed)
I'm getting:
Completed ActivityCount CompletedTimestamp
2012-09-25 300 2012-09-25 00:00:00
2012-10-02 764 2012-10-02 00:00:00
2012-10-08 379 2012-10-08 00:00:00
2012-10-17 659 2012-10-17 00:00:00
2012-10-22 382 2012-10-22 00:00:00
some are 7 days apart, others are 6 days apart, others are 5.... and one is 9?
Why does it group the dates by such strange intervals instead of just 7 days?
The week function does not count the difference of the dates.
The week function returns the week number of a date. If you group by it, then in the group will be dates at the start and end of the week and in bettween. The difference betwween the single dates can be greater than 7 days or less.
The answer, as alluded to by juergen d, was to aggregate the date column -- use min or max depending on whether you want to the first day or last day of the week used as the consistent interval; e.g.:
SELECT MIN(Completed), COUNT( DISTINCT Table1.ID ) AS ActivityCount FROM Table1 JOIN Table1Items ON Table1.ID = Table1Items.ID JOIN database_database.Table2 ON Table2.Item = Table1Items.Item WHERE Completed != '0000-00-00' AND Completed >= '2012-09-25' AND Completed <= '2012-10-25' GROUP BY WEEK( Completed)