Checking if new interval overlaps - MySQL (or PHP)

折月煮酒 提交于 2019-12-30 23:28:39

问题


I've been thinking on how I can simplify the problem presented here. Complex MySQL Query - Checking for overlapping DATE intervals

At it's heart, minus all the fancy magic with DATES this is simply a problem of checking for overlapping intervals. After all dates can be thought of as numbers and it may make the logic easier. Imagine the following table:

Schedules
schedule_id     |     start     |       end 
1               |       1       |       3
2               |       4       |       7                
3               |       8       |       13
4               |      15       |       16
5               |      18       |       24
6               |      25       |       28

I'm trying to insert a new interval such that [a,b] do not overlap with any other interval. The considerations:

  • Yes, I can pull the whole table into an array and do an O(N) search on it. That's boring.
  • I prefer to do this in MySQL so I don't have to pull down what can be an arbitrarily large table every time.

See the following image. This represents the bounds of what can and can not be inserted. http://i.stack.imgur.com/jE59w.png


回答1:


Using folowing abbreviations:

  • [old] := existing range
  • [new] := inserting range
  • OS := (old) existing_range.start
  • OE := (old) existing_range.end
  • NS := (new) inserting_range.start
  • NE := (new) inserting_range.end

the condition for overlaping of two ranges (old and new) is: (OS < NE) AND (OE > NS)

While the solution might be not trivial, its not that difficult to get there:

There is no overlaping if the new range is completly before or after the existing range: [new] <= [old] OR [old] <= [new] and that means that:

(NE <= OS) OR (OE <= NS)

Negotiating this statement we get the condition for overlaping:

!( (NE <= OS) OR (OE <= NS) )

Now using De Morgan's law we can write it as

!(NE <= OS) AND !(OE <= NS)

And this is equivalent to

(NE > OS) AND (OE > NS)

wich can be rewriten as

(OS < NE) AND (OE > NS)

Now we can find all overlaping ranges using

SELECT o.*
FROM Schedules o
WHERE o.start < :new_end
  AND o.end   > :new_start



回答2:


You can phrase an insert as:

insert into schedules(start, end)
    select s, e
    from (select $start s, $end as e) t
    where not exists (select 1
                      from schedules s2
                      where s.start <= t.end and s.end >= t.start
                     );

This will only insert the value if it doesn't overlap with an existing row in the table.



来源:https://stackoverflow.com/questions/34416146/checking-if-new-interval-overlaps-mysql-or-php

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!