In mysql, how can I check if two date ranges overlap?
I have this:
Note: We have that p.date_started <= p.date_finished
but dateA
can be equal to dateB
or smaller than dateB
or bigger than dateB
.
CODE:
$query = "SELECT u.first_name, u.last_name, u.avatar_filename, p.id, p.user_id, p.address, p.topic, p.latitude, p.longitude, d.name AS department_name
FROM user u
JOIN placement p ON p.user_id=u.id
JOIN department d ON d.id = u.department_id
WHERE p.active=1 AND (('{$dateA}' BETWEEN p.date_started AND p.date_finished) OR
('{$dateB}' BETWEEN p.date_started AND p.date_finished) OR
(p.date_started BETWEEN '{$dateA}' AND '{$dateB}') OR
(p.date_finished BETWEEN '{$dateA}' AND '{$dateB}'))";
Is there a better way of doing this?
Thanks
Figure 5 – Date ranges do not overlap. It's amazingly simple but powerful! In the example file I created, every date range has an end date.
Overlap = min(A2, B2) - max(A1, B1) + 1. In other words, the overlap of two integer intervals is a difference between the minimum value of the two upper boundaries and the maximum value of the two lower boundaries, plus 1.
With SUMPRODUCT we can check if each start date is less than any of the end dates in the table AND, if each end date is greater than any of the start dates in the table. If the dates on each row meets this criteria for more than one set of dates in the table, then we know there are overlapping dates.
let's pick the big date SELECT ID, EMP_ID, [START DATE], MAX(END DATE) FROM (SELECT ID, EMP_ID, TEAM, [END DATE], MIN([START DATE]) [START DATE] FROM my_table GROUP BY ID, EMP_ID, END_DATE ) a GROUP BY ID, EMP_ID, [START DATE] -- Now we are done with similar end date and similar start date -- At this point I will write ...
If we are guaranteed that date_started
, datefinished
, $DateA
and $DateB
are not NULL, and we're guaranteed that date_started
is not greater than date_finished
...
`s` represents `date_started`
`f` represents `date_finished`
`a` represents the smaller of `$DateA` and `$DateB`
`b` represents the larger of `$DateA` and `$DateB`
Visually:
s-----f overlap
-----+-----+----- -------
a-b | | NO
a---b | YES
a-----b | YES
a---------b YES
a-----------b YES
a---b | YES
a-----b YES
a-------b YES
| a-b | YES
| a---b YES
| a-----b YES
| a-b YES
| | a-b NO
We can easily detect when there's no "overlap" of the ranges:
( a > f OR b < s )
And we can easily negate that to return "true" when there is an "overlap":
NOT ( a > f OR b < s )
Converting that to SQL:
NOT ( GREATEST('{$dateA}','{$dateB}') < p.date_started
OR LEAST('{$dateA}','{$dateB}') > p.date_finished
)
A -> B
represent one range and a -> b
represent another range.
overlap scenario 1
A B
*----------*
a b
*----------*
overlap scenario 2
A B
*----------*
a b
*----------*
overlap scenario 3
A B
*----------*
a b
*---------------------*
overlap scenario 4 - is when a or b is same as A or B
a == A || a == B || b == A || b == B
Different scenarios would get fulfilled by checking against does four scenarios.
Our logic should check for all scenarios.
(scenario 1 (+4) || scenario 2 (+4) || scenario 3 )
(a >= A && a <= B) || (b >= A && b <= B) || (a < A && b > B)
You can cover all date overlapping cases even when to-date in database can possibly be null as follows:
SELECT * FROM `tableName` t
WHERE t.`startDate` <= $toDate
AND (t.`endDate` IS NULL OR t.`endDate` >= $startDate);
This will return all records that overlaps with the new start/end dates in anyway.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With