在MySQL中,如果我有一个日期范围列表(范围开始和范围结束)。 e.g。
10/06/1983 to 14/06/1983
15/07/1983 to 16/07/1983
18/07/1983 to 18/07/1983
我想检查另一个日期范围是否包含列表中已有的任何范围,我该怎么做?
e.g。
06/06/1983 to 18/06/1983 = IN LIST
10/06/1983 to 11/06/1983 = IN LIST
14/07/1983 to 14/07/1983 = NOT IN LIST
答案 0 :(得分:405)
这是一个经典问题,如果颠倒逻辑,它实际上更容易。
让我举个例子。
我会在这里发布一段时间,以及其他时期的所有不同变化以某种方式重叠。
|-------------------| compare to this one
|---------| contained within
|----------| contained within, equal start
|-----------| contained within, equal end
|-------------------| contained within, equal start+end
|------------| not fully contained, overlaps start
|---------------| not fully contained, overlaps end
|-------------------------| overlaps start, bigger
|-----------------------| overlaps end, bigger
|------------------------------| overlaps entire period
另一方面,让我发布所有不重叠的内容:
|-------------------| compare to this one
|---| ends before
|---| starts after
因此,如果您简单地将比较缩小为:
starts after end
ends before start
然后你会找到所有那些不重叠的东西,然后你会找到所有不匹配的句号。
对于最终的NOT IN LIST示例,您可以看到它符合这两个规则。
您需要确定以下时段是否在您的范围之内或之外:
|-------------|
|-------| equal end with start of comparison period
|-----| equal start with end of comparison period
如果你的表有名为range_end和range_start的列,这里有一些简单的SQL来检索所有匹配的行:
SELECT *
FROM periods
WHERE NOT (range_start > @check_period_end
OR range_end < @check_period_start)
请注意 NOT 。由于这两个简单的规则找到了所有不匹配的行,一个简单的NOT会将它反转为:如果它不是不匹配的行之一,它必须是其中一个匹配的。
在这里应用简单的反转逻辑来摆脱NOT,你最终会得到:
SELECT *
FROM periods
WHERE range_start <= @check_period_end
AND range_end >= @check_period_start
答案 1 :(得分:8)
将您的示例范围从06/06/1983改为18/06/1983并假设您的范围有开始和结束列,您可以使用像这样的条款
where ('1983-06-06' <= end) and ('1983-06-18' >= start)
即。检查测试范围的开始是在数据库范围结束之前,并且测试范围的结束是在数据库范围之后或之后。
答案 2 :(得分:4)
如果您的RDBMS支持OVERLAP()函数,那么这变得微不足道 - 不需要自行开发的解决方案。 (在Oracle中,它显然有效,但没有记录)。
答案 3 :(得分:0)
在您的预期结果中,您说
06/06/1983至18/06/1983 =在列表中
但是,此期间不包含也不包含在您的表格(不是列表!)中的任何期间。但是,它确实与1983年6月6日至1985年6月14日期间重叠。
你可能会发现Snodgrass书(http://www.cs.arizona.edu/people/rts/tdbbook.pdf)很有用:它早于mysql,但时间的概念没有改变; - )
答案 4 :(得分:0)
我在MySQL中创建了处理这个问题的函数。只需在使用前将日期转换为秒。
DELIMITER ;;
CREATE FUNCTION overlap_interval(x INT,y INT,a INT,b INT)
RETURNS INTEGER DETERMINISTIC
BEGIN
DECLARE
overlap_amount INTEGER;
IF (((x <= a) AND (a < y)) OR ((x < b) AND (b <= y)) OR (a < x AND y < b)) THEN
IF (x < a) THEN
IF (y < b) THEN
SET overlap_amount = y - a;
ELSE
SET overlap_amount = b - a;
END IF;
ELSE
IF (y < b) THEN
SET overlap_amount = y - x;
ELSE
SET overlap_amount = b - x;
END IF;
END IF;
ELSE
SET overlap_amount = 0;
END IF;
RETURN overlap_amount;
END ;;
DELIMITER ;
答案 5 :(得分:0)
查看以下示例。这对你有帮助。
SELECT DISTINCT RelatedTo,CAST(NotificationContent as nvarchar(max)) as NotificationContent,
ID,
Url,
NotificationPrefix,
NotificationDate
FROM NotificationMaster as nfm
inner join NotificationSettingsSubscriptionLog as nfl on nfm.NotificationDate between nfl.LastSubscribedDate and isnull(nfl.LastUnSubscribedDate,GETDATE())
where ID not in(SELECT NotificationID from removednotificationsmaster where Userid=@userid) and nfl.UserId = @userid and nfl.RelatedSettingColumn = RelatedTo
答案 6 :(得分:0)
CREATE FUNCTION overlap_date(s DATE, e DATE, a DATE, b DATE)
RETURNS BOOLEAN DETERMINISTIC
RETURN s BETWEEN a AND b or e BETWEEN a and b or a BETWEEN s and e;
答案 7 :(得分:0)
在MS SQL上试试这个
WITH date_range (calc_date) AS (
SELECT DATEADD(DAY, DATEDIFF(DAY, 0, [ending date]) - DATEDIFF(DAY, [start date], [ending date]), 0)
UNION ALL SELECT DATEADD(DAY, 1, calc_date)
FROM date_range
WHERE DATEADD(DAY, 1, calc_date) <= [ending date])
SELECT P.[fieldstartdate], P.[fieldenddate]
FROM date_range R JOIN [yourBaseTable] P on Convert(date, R.calc_date) BETWEEN convert(date, P.[fieldstartdate]) and convert(date, P.[fieldenddate])
GROUP BY P.[fieldstartdate], P.[fieldenddate];
答案 8 :(得分:0)
使用BETWEEN sql语句的另一种方法
期间包括:
SELECT *
FROM periods
WHERE @check_period_start BETWEEN range_start AND range_end
AND @check_period_end BETWEEN range_start AND range_end
不包括期间:
SELECT *
FROM periods
WHERE (@check_period_start NOT BETWEEN range_start AND range_end
OR @check_period_end NOT BETWEEN range_start AND range_end)
答案 9 :(得分:-2)
SELECT *
FROM tabla a
WHERE ( @Fini <= a.dFechaFin AND @Ffin >= a.dFechaIni )
AND ( (@Fini >= a.dFechaIni AND @Ffin <= a.dFechaFin) OR (@Fini >= a.dFechaIni AND @Ffin >= a.dFechaFin) OR (a.dFechaIni>=@Fini AND a.dFechaFin <=@Ffin) OR
(a.dFechaIni>=@Fini AND a.dFechaFin >=@Ffin) )