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
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;
SELECT *
FROM mytable a
JOIN mytable b ON (
a.name != b.name
AND func_time_overlap(a.starttime, a.endtime, b.starttime, b.endtime)
);
下面是函数定义
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时区(应该如此),则可以使用此
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;
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
)
SELECT * from Shedulles a
where exists
( select 1 from Shedulles b
where
a.ShedulleId != b.ShedulleId
and ( a.DateFrom between b.DateFrom and b.DateTo
or a.DateTo between b.DateFrom and b.DateTo
or b.DateFrom between a.DateFrom and a.DateTo )
and a.DateFrom != b.DateTo
and b.DateFrom != a.DateTo
);
或者这一个
SELECT DISTINCT a.* FROM Shedulles a
JOIN Shedulles b
ON
a.ShedulleId != b.ShedulleId
and ( a.DateFrom between b.DateFrom and b.DateTo
or a.DateTo between b.DateFrom and b.DateTo
or b.DateFrom between a.DateFrom and a.DateTo )
and a.DateFrom != b.DateTo
and b.DateFrom != a.DateTo
5条答案
按热度按时间hgtggwj01#
这是一个查询模式,我在很多年前就找到了答案:
为了找到“任何重叠”,您将时间框架的“相反”两端相互比较。这是我不得不拿出笔和纸,并绘制相邻的范围,以实现边缘情况归结为这种比较。
如果要防止任何行重叠,请将此查询的变体放入触发器中:
uujelgoq2#
我想要一个通用函数来检查两个时间范围是否重叠,这也适用于计划在午夜之前开始并在午夜之后结束的情况,例如“17:00:00-03:00:00”和“14:00:00-01:00:00”应该重叠,所以我修改了solution by Bohemian
您可以按如下方式使用此函数
或者像你这样
下面是函数定义
我没有使用
TIME(CAST(a_start AS DATETIME));
将时间间隔转换为时间,因为它默认使用当前日期,可能会受到数据库时区设置的影响,想象一下第二天DST发生变化。如果您的数据库使用的是UTC时区(应该如此),则可以使用此
lvjbypge3#
试试这个:
ig9co6j14#
试试这个,对我有用
或者这一个
kkih6yb85#
在PHP中很简单。按开始时间对列表进行排序,然后检查 * 连续 * 条目是否重叠:
MySQL中的等效方法更复杂,除非您使用MySQL 8.0(或MariaDB 10.2),因为需要
LEAD()
或LAG()
来查看相邻行。