Answers:
这是一个经典问题,如果您逆转逻辑,实际上会更容易。
让我举一个例子。
我将在此处发布一个时间段,以及其他时间段的所有不同变体以某种方式重叠。
|-------------------| 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
那么您将找到所有不重叠的部分,然后找到所有不匹配的期间。
对于最后一个“不在清单中”示例,您可以看到它与这两个规则匹配。
您需要确定以下时间段是否在您的范围之内或之外:
|-------------|
|-------| 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
在您的预期结果中,您说
1983年6月6日至1983年6月18日=清单
但是,此期间不包含也不包含在期间表(未列出!)中的任何期间。但是,它与10/06/1983到14/06/1983重叠。
您可能会发现Snodgrass的书(http://www.cs.arizona.edu/people/rts/tdbbook.pdf)很有用:它早于mysql,但是时间的概念没有改变;-)
我创建了函数来处理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 ;
看下面的例子。这将对您有所帮助。
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];
使用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) )