我在一系列带有各种相关表的SQLite数据库中获得了一些数据。每个表都有一个开始和结束日期列,表示每个记录有效的日期范围。 SQLite数据库主要使用C#和System.Data.SQLite库进行访问。
我希望能够查询日期范围重叠或不重叠的联接。我发现加入数据时它们的重叠非常简单:
SELECT a.field, max(a.start, b.start) as start, min(a.end, b.end) as end
FROM a
INNER JOIN b
ON a.field = b.field AND NOT(a.start > b.end OR b.start > a.end);
但我不知道如何获得没有匹配b的时间段。在没有重叠的地方很容易获得记录:
SELECT a.field, a.start, a.end
FROM a
LEFT JOIN b
ON a.field = b.field AND NOT(a.start > b.end OR b.start > a.end)
WHERE b.field is NULL;
但是他们重叠的地方怎么样,或者b分成两个记录呢?将日期范围显示为时间轴,如何在下面显示的c
关系中获得a-b=c
(这些行代表表a
和b
中各个记录的日期范围,以及结果集c
)
a: |-----------------| |--------| |--------||-----|
b: |---| |--------|
c: |-----| |-----| |-----| |---||-----|
或者更好的是,是否有一些我不知道的库,扩展,命令,或其他可用于简化这些查询的解决方案?能为我处理凌乱日期范围操作的东西吗?
答案 0 :(得分:2)
有几点指示:
确保您的值是基于UTC的时间戳,或仅是整个日历日期。这是为了避免时区和夏令时问题。
使用半开区间[start, end)
。这将避免两个相邻范围包含相同值的问题。换句话说:
start <= value < end
start <= value && end > value
您可以考虑使用Noda Time。它有一个Interval
类型,可以很好地代表它。但是,它目前没有定义很多操作。
您可能还会考虑使用Time Period Library for .NET,其中定义了大量个操作。请注意,您使用的所有DateTime
值都DateTimeKind.Utc
为.Kind
值。如果您尝试使用本地种类,它将无法正常运行。换句话说,请勿通过DateTime.Now
。
当然,不需要使用任何库。您始终可以定义自己的结构或类来包含范围。没有内置任何东西,没有。
您编写的查询适用于检测重叠,但您可能需要稍微简化一下:
而不是:NOT(a.start > b.end OR b.start > a.end)
这样做:a.start < b.end AND b.start < a.end
这在逻辑上是等效的,但作为查询会稍微好一些。
我不知道如何直接回答你的问题。你问的问题并不十分清楚。具体来说,在最后一个示例中,表中是否已存在c
的两个范围并且您想要返回它们?或者你想从a和b之间的计算构造它们?如果是后者,最好在C#中执行该部分,而不是在SQL中执行。
答案 1 :(得分:0)
使用Time Period Library for .NET,您可以将时间段读入集合并应用 TimeGapCalculator , TimePeriodCombiner 或 TimePeriodSubtractor 实用程序评估所需的差距和重叠。