This question already has answers here:
Check overlap of date ranges in MySQL
(8 answers)
Closed 4 years ago.
I know this can be accomplished on the backend - but I'm wondering if there's any native, or efficient MySQL function that can be used to check if a given time variable falls within a range covered by two time variables (a start_time, and an end_time).
I have a database currently set up which looks like the following;
+----+--------+------------+----------+
| id | job_id | start_time | end_time |
+----+--------+------------+----------+
| 1 | 40 | 13:00:00 | 14:00:00 |
| 2 | 44 | 14:45:00 | 15:00:00 |
| 3 | 45 | 15:10:00 | 15:30:00 |
+----+--------+------------+----------+
The backend accepts a start_time, and an end_time with a job_id - and then it looks to see if it can be fit in anywhere. So for example, given a start_time of 13:30:00, and an end_time of 13:45:00, the backend should reject this job request as there is no time available for it (it would overlap with the entry at id 1.)
However, if a job is submitted with a start_time of 14:10:00, and an end time of 14:20:00, it should be accepted - as this does not overlap with any existing tasks.
The following query is great to tell if a job can be submitted for say, 13:00:00 until 14:00:00 (an exact duplicate of id 1);
SELECT * WHERE start_time >= '13:00:00' AND end_time <= '14:00:00';
But if the start_time becomes 13:01:00, then the query falls down - as the start_time is less than 13:01:00, at 13:00:00. So it'll get approved for insertion, as the above query will return no overlapping results.
If we change the query to an OR clause on end_time, then literally any job that doesn't end before 14:00:00 would be rejected.
Can anyone suggest a simple way of taking an input variable of a time type, and checking if it falls within range of all available start_time, and end_time variables as noted in the db above?
I would suggest you to check in in one of the following ways: hours/minutes/seconds
SELECT * FROM timetableTemp
WHERE HOUR(TIMEDIFF(start_time, end_time))>=1
AND start_time >=start_time
OR use BETWEEN function
SELECT * FROM timetableTemp
WHERE '13:00:00' BETWEEN start_time AND end_time
support:
SELECT SECOND(TIMEDIFF("13:10:11", "13:10:10")) AS Seconds; -- 1
SELECT MINUTE(TIMEDIFF("13:10:11", "13:10:10")) AS Minutes; -- 0
SELECT HOUR(TIMEDIFF("13:00:00", "14:00:00")) AS Hours; -- 1
Let's suppose you have a new job with start of new_start and end of new_end. Assume your existing list is in order of time of each job entry, and they are all non-overlapping.
What you'd need to check to see if the new job fits in the list without overlap would be:
new_end is less than the start_time of the first list entry, in
which case the new entry fits at the beginning, OR
new_start is
greater than the end_time of the last list entry, in which case the
new entry fits at the end, OR
For some in-between list entry,
new_start is greater than the end_time and new_end is less than
the start_time of the following list entry, in which case the new
entry fits in between this entry and the next.
Related
For a user management software I try to implement a feature, where employees can provide their availabilities at a specific location.
I got a form:
Location [> LOC1; LOC2, LOC3]
Start [YYYY-MM-DD] [HH:ii]
End [YYYY-MM-DD] [HH:ii]
Full day [X]
Frequence [> ONCE; EVERY MONDAY; EVERY 2ND WEEK]
This goes to the follow database table scheme:
eid | lid | start | end | allay | frequence
Where frequence is NULL for once, 1 for every day of the week provided by start value, and 2 for every 2. week.
With that I have the following logic:
Where Y is always greater than X (both include date and time).
As far as I can think of, this is covering all possible cases. Logically I can use that to query a presence, but I am struggling with the dates, that are available in infinity. My logic is, if start and end date are the same, and frequence is not null, then this row will be available always and in infinity.
E.g. employee is available from 2022-03-31 10:00:00 to 2022-03-31 18:00:00, allday 0, frequence 1, then he is available every Thursday between 10am and 6pm.
So far, so good, so logic, but when I now want to use that, I am struggling, and I want to ask you experts, if this is the right way to implement that.
Issues I see:
Query: Is an employee available at a specific date / time? For the infinity cases, I don't know hot to query that.
If there is a frequent rule in place, and I add another rule that overwrites the same rule, I don't know how to avoid duplicates.
E.g.:
2022-03-31 10:00:00 2022-03-31 18:00:00 0 1
2022-04-14 17:00:00 2022-04-17 20:00:00 0 null
In consequence at 2022-04-14 the employee is available from 10am to 8pm.
And last issue I see:
If I want to build a calendar that shows a specific month (e.g. October 22) and I want all availabilities in that month, how can I get every single date and time value for every single day in that month for rows like:
2022-03-31 10:00:00 2022-03-31 18:00:00 0 1
2022-03-31 00:00:00 2022-12-31 00:00:00 1 2
As my database design is bad maybe, I don't see a value in adding a mysql fiddle with some sample data. But anyhow if you wish to see that, let me know, and I'll update my question.
What I'd prefer maybe, is, to add a row for every day where an employee is available. This would be easier and more intuitive and I can handle to logic once, when I add an availability, but how would I handle infinite dates there? I could assume that the app will not be used longer than 20y and add every date in the next 20 years?!
I have a table tblMatch :
+---------+---------------------+------------------+-----------+
| ID | start_date | end_date | status |
+---------+---------------------+------------------+-----------+
| 1 | 2017-12-09 03:23 | 2017-12-10 03:23 | 1 |
+---------+---------------------+------------------+-----------+
| ... | ... | ... | 1 |
+---------+---------------------+------------------+-----------+
| 1000000 | 2017-12-22 15:12 | 2017-12-30 15:12 | 1 |
+---------+---------------------+------------------+-----------+
When I insert a row, I create one event too.
Event will change status to 0 if the match is ended.
CREATE EVENT test_event_increment_number
ON SCHEDULE AT end_date
ON COMPLETION NOT PRESERVE
DO
UPDATE tblMatch SET status = 0 WHERE ID = increment_number;
If tblMatch has 100 million matches :
Does it effect server performance?
Is it bad or good idea to create a lot of events?
Create just 1 event that runs daily and closes the matches expiring that day.
CREATE EVENT test_event
ON SCHEDULE AT 1 every day
STARTS (TIMESTAMP(CURRENT_DATE) + INTERVAL 1 DAY)
DO
UPDATE tblMatch SET status = 0 WHERE end_date = CURRENT_DATE;
UPDATE
If you want to time your events at a minute level, then either change the frequency of your event to minute level and use minute level when determining if a match needs closing, or completely drop the status field and just use the end_date field's value compared to now() to determine if the event is closed. The latter is a better way.
I Create 25 events is different time. After a while, the database takes all the resources, and reboots.
CREATE EVENT `auction_event_46709`
ON SCHEDULE EVERY 5 MINUTE STARTS '2017-12-07 10:23:03'
ON COMPLETION NOT PRESERVE ENABLE
DO CALL auction_update_price(46709)
Procedure:
CREATE PROCEDURE `auction_update_price`( IN p_id INT )
BEGIN
DECLARE cur_price INT;
DECLARE stp_price INT;
DECLARE st_price INT;
DECLARE str_price INT;
SELECT current_price, step_price, stop_price, start_price INTO cur_price, stp_price, st_price, str_price FROM product_to_auction WHERE product_id = p_id;
IF( cur_price - stp_price > st_price ) THEN
UPDATE product_to_auction SET current_price = current_price - step_price WHERE current_price > stop_price AND product_id = p_id;
ELSE
UPDATE product_to_auction SET current_price = str_price WHERE product_id = p_id;
END IF;
END
How to Fix?
Don't use events at all for this sort of operation. Certainly don't use many events for it. Instead, use a query (or view) that takes your end_date into account and determines your status value dynamically based on date. For example, to retrieve a particular item by id, do this.
SELECT id, start_date, end_date
CASE WHEN end_date <= CURDATE() THEN 0 ELSE status END AS status
FROM tblMatch
WHERE id = something
This query returns the row from the table, along with the status value based on the moment you run the query. (I set it up so items with status = 0 are always marked as expired never mind the current time.)
If you want all the items with status 1 (meaning non expired) do this:
SELECT id, start_date, end_date, 1 AS status
FROM tblMatch
WHERE end_date < CURDATE ()
AND status = 1
If you MUST use an event, you can run it once a day, sometime after midnight to reset the status columns of all expiring rows to 0, with a query like this.
UPDATE tblMatch SET status = 0 WHERE status = 1 AND end_date < CURDATE();
(I prefer to run daily update queries shortly after 03:00 local time. Why? I'm located in the USA, and our daylight saving time switchover is done, twice a year, at 02:00 local time. Doing daily updates after 03:00 ensures they'll still work properly on switchover days. )
For these queries to be efficient, you need a compound index on (status, end_date)
I have Mysql table:
id | deadline | days
1 | 1423695600 | 0
2 | 1426705199 | 1,2,3
I want: if days filed is 0, change deadline to today's date but keep old hour and minute
I have tried but i dont know how to bulid query
SELECT id IF(p.days != 0, deadline) as deadline, days FROM posts
I think you want to convert from unix time stamps to regular date times for this operation:
select addtime(date(now()), time(from_unixtimestamp(deadline))
You can get back a unix time stamp:
select unix_timestamp(addtime(date(now()), time(from_unixtimestamp(deadline)))
Try Case:
SELECT stock.name,
CASE
WHEN stock.quantity <20 THEN 'Buy urgent'
ELSE 'There is enough'
END
FROM stock
I have a MySQL table containing a column to store time and another to store a value associated with that time.
time | value
------------
1 | 0.5
3 | 1.0
4 | 1.5
.... | .....
The events are not periodic, i.e., the time values do not increment by fix interval.
As there are large number of rows (> 100000), for the purpose of showing the values in a graph I would like to be able to aggregate (mean) the values for an interval of fixed size over the entire length of time for which the data is available. So basically the output should consist of pairs of interval and mean values.
Currently, I am splitting the total time interval into fixed chunks of time, executing individual aggregate queries for that interval and collecting the results in application code (Java). Is there a way to do all of these steps in SQL. Also, I am currently using MySQL but am open to other databases that might support an efficient solution.
SELECT FLOOR(time / x) AS Inter, AVG(value) AS Mean
FROM `table`
GROUP BY Inter;
Where x is your interval of fixed size.
I've usually solved this through a "period" table, with all the valid times in it, and an association with the period on which I report.
For instance:
time day week month year
1 1 1 1 2001
2 1 1 1 2001
....
999 7 52 12 2010
You can then join your time to the "period" table time, and use AVG.
I have an table in my DB something like this:
----------------------------------------------------------
| event_id | date | start_time | end_time | duration |
----------------------------------------------------------
| 1 | 2011-05-13 | 01:00:00 | 04:00:00 | 10800 |
| 2 | 2011-05-12 | 17:00:00 | 01:00:00 | 28800 |
| 3 | 2011-05-11 | 11:00:00 | 14:00:00 | 10800 |
----------------------------------------------------------
This sample data doesn't give a totally accurate picture, there is typically events covering every hour of every day.
The date always refers to the start_time, as the end_time can sometimes be the following day.
The duration is in seconds.
SELECT *
FROM event_schedules
WHERE (
date = CURDATE() //today
OR
date = DATE_SUB(CURDATE(), INTERVAL 1 DAY) //yesterday
)
// and ended before now()
AND DATE_ADD(CONCAT(date, ' ', start_time), INTERVAL duration SECOND) < NOW()
ORDER BY CONCAT(date, ' ', start_time) DESC
LIMIT 1
I have a clause in there, the OR'ed clause in brackets, that is unnecessary. I hoped that it might improve the query time, by first filtering out any "events" that do not start today or yesterday. The only way to find the most recent "event" is by ordering the records and taking the first. By adding this extra unnecessary clause am I actually reducing the list of records that need to be ordered? If it does I can't imagine the optimizer being able to make this optimization, most other questions similar to this talk about the optimizer.
Be careful when adding filters to your WHERE clause for performance. While it can reduce the overall number of rows that need to be searched, the actual filter itself can cause a higher cost if it's filtering a ton of records and not using an index. In your case, if the column date is indexed, you'll probably get better performance because it can use the index in the OR part, where as it can't in the other parts because it's being called as a function. Also, can you have future dates? If not, why don't you change the OR to
date > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
The order of the where clause does affect the way the sql engine gets the results.
Many of them have a way to view what the engine does with a query. If you're using sqlserver look for "show estimated execution plan" in your client tool. Some have a verb like "explain" that can be used to show how the engine treats a query.
Well, the optimizer in the query engine is a big part of any query's performance, or the relative performance of two equivalent statements.
You didn't tell us if you ran the query with and without the extra where. There may be a performance difference, there may not.
My guess is that the LIMIT has a lot to do with it. The engine knows this is a "one and done" operation. Without the WHERE, sorting is an NlogN operation, which in this special case can be made linear with a simple scan of the dates to find the most recent.
With the WHERE, you're actually increasing the number of steps it has to perform; either it has to fully order the table (NlogN) and then scan that list for the first record that matches the WHERE clause (linear worst-case, constant best-case), OR it has to filter by the WHERE (linear), then scan those records again to find the max date (linear again). Whichever one turns out faster, they're both slower than one linear scan of the list for the most recent date.