代码之家  ›  专栏  ›  技术社区  ›  Pierre de LESPINAY

检查mysql中日期范围的重叠

  •  68
  • Pierre de LESPINAY  · 技术社区  · 15 年前

    此表用于存储会话(事件):

    CREATE TABLE session (
      id int(11) NOT NULL AUTO_INCREMENT
    , start_date date
    , end_date date
    );
    
    INSERT INTO session
      (start_date, end_date)
    VALUES
      ("2010-01-01", "2010-01-10")
    , ("2010-01-20", "2010-01-30")
    , ("2010-02-01", "2010-02-15")
    ;
    

    我们不想在范围之间发生冲突。
    假设我们需要插入 2010-01-05 2010-01-25 .
    我们想知道冲突的会话。

    我的问题是:

    SELECT *
    FROM session
    WHERE "2010-01-05" BETWEEN start_date AND end_date
       OR "2010-01-25" BETWEEN start_date AND end_date
       OR "2010-01-05" >= start_date AND "2010-01-25" <= end_date
    ;
    

    结果如下:

    +----+------------+------------+
    | id | start_date | end_date   |
    +----+------------+------------+
    |  1 | 2010-01-01 | 2010-01-10 |
    |  2 | 2010-01-20 | 2010-01-30 |
    +----+------------+------------+
    

    有更好的办法吗?


    fiddle

    6 回复  |  直到 8 年前
        1
  •  129
  •   A.L    11 年前

    我曾经写过一个日历应用程序,有这样一个查询。我想我用了这样的东西:

    ... WHERE new_start < existing_end
          AND new_end   > existing_start;
    

    更新 这肯定有效((ns,ne,es,ee)=(new_start,new_end,existing_start,existing_end):

    1. ns-ne-es-ee:不重叠且不匹配(因为ne<es)
    2. ns-es-ne-ee:重叠和匹配
    3. es-ns-ee-ne:重叠和匹配
    4. es-ee-ns-ne:不重叠且不匹配(因为ns>ee)
    5. es-ns-ne-ee:重叠和匹配
    6. ns-es-ee-ne:重叠和匹配

    这里是一个 fiddle

        2
  •  23
  •   Yasen    11 年前
    SELECT * FROM tbl WHERE
    existing_start BETWEEN $newStart AND $newEnd OR 
    existing_end BETWEEN $newStart AND $newEnd OR
    $newStart BETWEEN existing_start AND existing_end
    
    if (!empty($result))
    throw new Exception('We have overlapping')
    

    这3行sql子句涵盖了所需的4种重叠情况。

        3
  •  13
  •   LordJavac    12 年前

    拉米的回答很好,但你可以再优化一点。

    SELECT * FROM tbl WHERE
    existing_start BETWEEN $newSTart AND $newEnd OR
    $newStart BETWEEN existing_start AND existing_end
    

    这将捕获范围重叠的所有四个场景,并排除不重叠的两个场景。

        4
  •  4
  •   Niraj Kumar    11 年前

    我也遇到过类似的问题。我的问题是在一系列被阻止的日期之间停止预订。例如,5月2日至5月7日期间,某个楼盘的预订被禁止。我需要找到任何重叠的日期来检测和停止预订。我的解决方案类似于lordjavac。

    SELECT * FROM ib_master_blocked_dates WHERE venue_id=$venue_id AND 
    (
        (mbd_from_date BETWEEN '$from_date' AND '$to_date') 
        OR
        (mbd_to_date BETWEEN  '$from_date' AND '$to_date')
        OR
        ('$from_date' BETWEEN mbd_from_date AND mbd_to_date)
        OR      
        ('$to_date' BETWEEN mbd_from_date AND mbd_to_date)      
    )
    *mbd=master_blocked_dates
    

    如果不行就告诉我。

        5
  •  1
  •   Mackraken    9 年前

    给定两个间隔,如(s1,e1)和(s2,e2),其中s1<e1和s2<e2
    可以这样计算重叠:

    SELECT 
         s1, e1, s2, e2,
         ABS(e1-s1) as len1,
         ABS(e2-s2) as len2,
         GREATEST(LEAST(e1, e2) - GREATEST(s1, s2), 0)>0 as overlaps,
         GREATEST(LEAST(e1, e2) - GREATEST(s1, s2), 0) as overlap_length
    FROM test_intervals 
    

    如果一个间隔在另一个间隔内,也将工作。

        6
  •  0
  •   Vishal Kumar Sahu    8 年前

    最近我也在同一个问题上苦苦挣扎,最后只迈出了一个简单的步骤(这可能不是一个好的方法或消耗内存的方法)。-

    SELECT * FROM duty_register WHERE employee = '2' AND (
    (
    duty_start_date BETWEEN {$start_date} AND {$end_date}
    OR
    duty_end_date BETWEEN {$start_date} AND {$end_date}
    )
    OR
    (
    {$start_date} BETWEEN duty_start_date AND duty_end_date
    OR
    {$end_date} BETWEEN duty_start_date AND duty_end_date)
    );
    

    这帮助我找到日期范围重叠的条目。

    希望这能帮助别人。

    推荐文章