比较date范围
在MySQL中,如果我有一个date范围的列表(范围开始和范围结束)。 例如
10/06/1983 to 14/06/1983 15/07/1983 to 16/07/1983 18/07/1983 to 18/07/1983
我想检查另一个date范围是否包含列表中的任何范围,我将如何做?
例如
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
这是一个经典的问题,如果你逆转逻辑,它实际上更容易。
让我举一个例子。
我会在这里张贴一段时间,其他时期的所有不同的变化都以某种方式重叠。
|-------------------| 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)
注意不在那里。 由于这两条简单的规则可以find所有不匹配的行,所以一个简单的NOT会反转它: 如果它不是不匹配的行之一,则它必须是匹配的行之一 。
在这里应用简单的反转逻辑来摆脱NOT,你将会得到:
SELECT * FROM periods WHERE range_start <= @check_period_end AND range_end >= @check_period_start
以您的示例范围为06/06/1983至18/06/1983,假设您的范围中有起始和结束的列,则可以使用这样的子句
where ('1983-06-06' <= end) and ('1983-06-18' >= start)
即检查testing范围的开始是否在数据库范围结束之前,并且testing范围的结束在数据库范围的开始之后或之后。
如果你的RDBMS支持OVERLAP()函数,那么这将变得微不足道 – 不需要本土解决scheme。 (在Oracle中,它显然有效,但没有logging)。
在你预期的结果你说
06/06/1983至18/06/1983 = IN LIST
但是,这个时期并不包含也不包含在你的表(不是列表)中的任何时期。 不过,它的确在10/06/1983至14/06/1983之间重叠。
你可能会发现Snodgrass的书( http://www.cs.arizona.edu/people/rts/tdbbook.pdf )有用:它在MySQL之前,但时间的概念没有改变;-)
我在MySQL中创build了处理这个问题的函数。 只需将date转换为使用前的秒数。
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 ;
看看下面的例子。 这对你有帮助。
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
在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];
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;
另一种方法是使用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)
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) )