我一直在思考如何简化这里提出的问题。复杂MySQL查询-检查重叠的日期间隔
从本质上讲,除去DATES的所有神奇之处,这只是一个检查重叠间隔的问题。毕竟,日期可以被认为是数字,这可能会让逻辑变得更容易。想象一下下表:
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]就不会与任何其他区间重叠。注意事项:
- 是的,我可以把整个表拉成一个数组,然后对它进行O(N)搜索。这很无聊
- 我更喜欢在MySQL中这样做,这样我就不必每次都拉下一个任意大的表
请参见下图。这表示可以插入和不能插入的范围。https://i.stack.imgur.com/jE59w.png
使用以下缩写:
- [old]:=现有范围
- [new]:=插入范围
- 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) )
现在使用德摩根定律,我们可以把它写成
!(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
您可以将插入短语表述为:
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
);
只有当值与表中的现有行不重叠时,才会插入该值。