【问题标题】:Checking if new interval overlaps - MySQL (or PHP)检查新间隔是否重叠 - MySQL(或 PHP)
【发布时间】:2016-03-28 17:39:12
【问题描述】:

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

本质上,减去 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 中执行此操作,因此我不必每次都拉下任意大的表。

见下图。这代表了可以插入和不能插入的界限。 http://i.stack.imgur.com/jE59w.png

【问题讨论】:

  • 您要插入什么间隔?如果你想要任何一个,那么选择最大的end 值并添加一个作为开始。然后,您可以将 end 设为任何更大的值。
  • @GordonLinoff - 我想插入一个任意间隔,这样就不会与任何其他间隔发生冲突。间隔可以是任意长度。根据我发布的图片,顶部的小间隔将是“有效的”,因为它不冲突,而在底部我试图代表我能想到的所有可能的冲突。基本上“我的新间隔是否遇到(或包含在)任何现有的东西?是否有任何东西遇到(或包含在)我的新间隔?”
  • SELECT * FROM Schedules WHERE start <= 17 AND end >= 16 将返回与您建议的插入冲突的所有间隔。
  • 这在蓝色的情况下是行不通的。当然我们SELECT * FROM schedules WHERE start <=14 and end >= 17 但是橙色包含在蓝色想要去的地方。这也不能避免 O(N) 查找。我从查询中得到了所有这些行,但我仍然必须对它们进行 O(N) 搜索(row1 是否重叠?row2 是否重叠,row3 是否重叠...)
  • 在蓝色的情况下,它将是WHERE start <= 17 AND end >= 14。它在所有情况下都有效。

标签: php mysql scheduling intervals


【解决方案1】:

使用以下缩写:

  • [旧] := 现有范围
  • [新] := 插入范围
  • 操作系统:=(旧)existing_range.start
  • OE := (old) existing_range.end
  • NS := (new) inserting_range.start
  • NE := (new) 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

【讨论】:

    【解决方案2】:

    您可以将插入语句表述为:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-23
      • 2016-06-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多