检查新间隔是否重叠 - MySQL(或 PHP)

Checking if new interval overlaps - MySQL (or PHP)

我一直在思考如何简化这里提出的问题。 Complex MySQL Query - Checking for overlapping DATE intervals

从本质上讲,除去 DATES 的所有花哨魔法,这只是一个检查重叠间隔的问题。毕竟日期可以被认为是数字,它可以使逻辑更容易。想象以下 table:

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

我正在尝试插入一个新区间,使 [a,b] 不与任何其他区间重叠。考虑因素:

见下图。这表示可以插入和不能插入的范围。 http://i.stack.imgur.com/jE59w.png

您可以将插入词表述为:

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
                     );

这只会插入不与 table 中的现有行重叠的值。

使用以下缩写:

  • [旧] := 现有范围
  • [新] := 插入范围
  • OS :=(旧)existing_range.start
  • OE :=(旧)existing_range.end
  • NS :=(新)inserting_range.start
  • NE :=(新)inserting_range.end

两个范围(旧的和新的)重叠的条件是:(OS < NE) AND (OE > NS)

虽然解决方案可能并不简单,但实现起来并不难:

如果新范围完全在现有范围之前或之后,则没有重叠:[new] <= [old] OR [old] <= [new] 这意味着:

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

协商这个语句我们得到重叠的条件:

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

现在使用De Morgan's law我们可以写成

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

这相当于

(NE > OS) AND (OE > NS)

可以改写为

(OS < NE) AND (OE > NS)

现在我们可以使用

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