首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >检查表中是否有时间重叠?

问检查表中是否有时间重叠?
EN

Stack Overflow用户
提问于 2011-07-04 12:31:31
回答 4查看 16.9K关注 0票数 26

我有一个包含以下字段的MySQL表:

  • name
  • starttime
  • endtime

starttime和endtime是MySQL TIME字段(而不是DATETIME)。我需要一种定期“扫描”表的方法,以查看表中的时间范围是否有任何重叠。如果有来自10:00-11:00的事件和来自10:30-11:30的另一个事件,我想要被提醒时间重叠的存在。

没有什么特别的,我只想知道是否存在重叠。

我将使用PHP来执行此操作。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2011-07-04 12:35:59

这是一种多年前我找到答案的查询模式:

代码语言:javascript
复制
SELECT *
FROM mytable a
JOIN mytable b on a.starttime <= b.endtime
    and a.endtime >= b.starttime
    and a.name != b.name; -- ideally, this would compare a "key" column, eg id

要找到“任何重叠”,您可以将时间框架的相反部分进行比较。这是我不得不拿出笔和纸,并画出相邻的范围,以认识到边缘的情况归结为这种比较。

如果要防止任何行重叠,请将此查询的变体放在触发器中:

代码语言:javascript
复制
create trigger mytable_no_overlap
before insert on mytable
for each row
begin
  if exists (select * from mytable
             where starttime <= new.endtime
             and endtime >= new.starttime) then
    signal sqlstate '45000' SET MESSAGE_TEXT = 'Overlaps with existing data';
  end if;
end;
票数 42
EN

Stack Overflow用户

发布于 2016-05-03 12:34:52

我想要一个泛型函数来检查两天重叠的时间范围是否也适用于这样的情况:时间表在午夜之前开始,然后结束,比如"17:00:00-03:00:00“和"14:00:00-01:00:00”应该重叠,所以我修改了solution by Bohemian。

使用此函数的步骤如下

代码语言:javascript
复制
SELECT func_time_overlap("17:00:00","03:00:00", "14:00:00","01:00:00")

或者在你这种情况下

代码语言:javascript
复制
SELECT *
FROM mytable a
JOIN mytable b ON (
    a.name != b.name 
    AND func_time_overlap(a.starttime, a.endtime, b.starttime, b.endtime)
);

以下是函数定义

代码语言:javascript
复制
CREATE FUNCTION `func_time_overlap`(a_start TIME, a_end TIME, b_start TIME, b_end TIME) 
RETURNS tinyint(1) 
DETERMINISTIC
BEGIN

-- there are only two cases when they don't overlap, but a lot of possible cases where they do overlap

-- There are two time formats, one is an interval of time that can go over 24 hours, the other is a daily time format that never goes above 24 hours
-- by default mysql uses TIME as an interval
-- this converts a TIME interval into a date time format

-- I'm not using `TIME(CAST(a_start AS DATETIME));` to convert the time interval to a time
-- because it uses the current day by default and might get affected by the timezone settings of the database, 
-- just imagine the next day having the DST change.
-- although the CAST should work fine if you use UTC

IF a_start >= 24 THEN 
    SET a_start = TIME(CONCAT(MOD(HOUR(a_start), 24),':',MINUTE(a_start),':',SECOND(a_start))); 
END IF;

IF b_start >= 24 THEN 
    SET b_start = TIME(CONCAT(MOD(HOUR(b_start), 24),':',MINUTE(b_start),':',SECOND(b_start))); 
END IF;

IF a_end > 24 THEN 
    SET a_end = TIME(CONCAT(MOD(HOUR(a_end), 24),':',MINUTE(a_end),':',SECOND(a_end))); 
END IF;

IF b_end > 24 THEN 
    SET b_end = TIME(CONCAT(MOD(HOUR(b_end), 24),':',MINUTE(b_end),':',SECOND(b_end))); 
END IF;


-- if the time range passes the midnight mark, then add 24 hours to the time
IF a_start >= a_end THEN 
    SET a_end = a_end + INTERVAL 24 HOUR; 
END IF;

IF b_start >= b_end THEN 
    SET b_end = b_end + INTERVAL 24 HOUR; 
END IF;

RETURN a_start < b_end AND a_end > b_start;


END

我不使用TIME(CAST(a_start AS DATETIME));将时间间隔转换为时间,因为默认情况下它使用当前日期,并且可能会受到数据库时区设置的影响,想象一下第二天会发生DST更改。

如果您的数据库正在使用UTC时区(正如它应该使用的那样),那么您可以使用以下内容

代码语言:javascript
复制
IF a_start >= 24 THEN 
    SET a_start = TIME(CAST(a_start AS DATETIME)); 
END IF;

IF b_start >= 24 THEN 
    SET b_start = TIME(CAST(b_start AS DATETIME)); 
END IF;

IF a_end > 24 THEN 
    SET a_end = TIME(CAST(a_end AS DATETIME));
END IF;

IF b_end > 24 THEN 
    SET b_end = TIME(CAST(b_end AS DATETIME));
END IF;
票数 3
EN

Stack Overflow用户

发布于 2011-07-04 12:46:47

试试这个:

代码语言:javascript
复制
declare @tempTbl table(RecID)

    insert into @tempTbl
    Select RecID
    from 
    (
    Select t.RecID from Table1 t,Table1 t1
    where t.StartTime between t1.StartTime AND t1.EndTime
    AND t.RecID <> t1.RecID  

    )
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6571538

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档