C# 分割重叠日期范围的最快方法
我在SQL DB表中有日期范围数据,它有以下三列(仅相关):C# 分割重叠日期范围的最快方法,c#,split,overlap,date-range,C#,Split,Overlap,Date Range,我在SQL DB表中有日期范围数据,它有以下三列(仅相关): ID(int-identity) 范围从(仅限日期) 范围至(仅限日期) 对于任何给定的日期范围,可能有任意数量的记录重叠(完全或部分重叠) 条件 每个ID较高的记录(较新的记录)优先于可能重叠(全部或部分)的较旧记录 范围至少为1天(RangeFrom和RangeTo相差一天) 因此,对于给定的日期范围(不超过5年),我必须 获取属于此范围的所有范围记录(全部或部分) 将这些重叠拆分为非重叠范围 返回这些新的非重叠范围 我的看法
(int-identity)ID
(仅限日期)范围从
(仅限日期)范围至
RangeFrom
和RangeTo
相差一天)ID=1
到ID=5
,它们以这种方式在视觉上重叠(日期实际上是不相关的,我可以用这种方式更好地显示这些重叠):
结果应该如下所示:
111111166666666666664444444444444444444444333333333555555555511111117777777
|666|666666|6666|
| | |4444|444|444444444444|4444444| |55555|55555|
| |222222|2222|222| |3333333|333333333|33333| | |7777777
1111111|111|111111|1111|111|111111111111|1111111|111111111|11111|11111|1111111|
1234567|890|123456|7890|123|4
1 -> 1
8 -> 1,6
11 -> 6,2,1
17 -> 6,4,2,1
21 -> 4,2,1
24 -> 4,1
...
结果实际上看起来就像我们从顶部看这些重叠,然后从这个自顶向下的视图中得到我们看到的ID
结果实际上会被转换成新的范围记录,所以旧的ID变得无关紧要。但将使用它们的范围从
和范围到
值(以及所有相关数据):
111111122222222222223333333333333333333333444444444555555555566666667777777
当然,这只是重叠范围的一个例子。对于任何给定的日期范围,它可以是从0条记录到X的任何内容。正如我们可以看到的那样,范围ID=2被4和6完全覆盖,因此它变得完全过时。实际上,您希望对数据进行堆栈,并从堆栈中选择最大值。我以前也曾实施过类似的方法,我们使用的方法比您要求的更灵活,因此可能不适合这样做: 具有用于管理记录的对象,并将每个记录添加到此对象。添加记录时,创建一个新的日期范围,并将该记录的值与该范围相关联。然后检查该范围是否与任何其他现有范围重叠。如果重叠,则为每个重叠创建一个新范围,并将重叠范围与新范围关联起来(取决于您是在添加每个范围时还是在单个过程中)。这可以在添加数据时完成,也可以在添加所有数据后在一次传递中完成 最后是一个包含唯一范围的对象,每个范围都有一个与之关联的值集合,有点像上面的图片 |666|666666|6666| | | |4444|444|444444444444|4444444| |55555|55555| | |222222|2222|222| |3333333|333333333|33333| | |7777777 1111111|111|111111|1111|111|111111111111|1111111|111111111|11111|11111|1111111| |666|666666|6666| | | |4444|444|444444444444|4444444| |55555|55555| | |222222|2222|222| |3333333|333333333|33333| | |7777777 1111111|111|111111|1111|111|111111111111|1111111|111111111|11111|11111|1111111| 然后,您可以为类提供一个扁平化函数(可能使用策略模式),该函数将具有值集合的唯一范围转换为具有单个值的唯一范围,这显然会将最终具有相同值的范围连接起来 您可能需要一个从每个唯一范围中选择最大值的类,但也可能需要选择最小值、求和、平均值、计数等。每个选项都可以通过传递不同的策略实现来完成
正如我所说的,这种方法可能比只选择最大值的方法效率低,因为在这种情况下,您不需要将所有值都保留在堆栈中,但正如我所记得的,实现是相当直接的。我不太确定这是否有用,但我将采用这种方法。。。 (为了便于理解,首先未进行优化…)
- 将表映射从[ID->range]转换为[date->list of ID]
111111166666666666664444444444444444444444333333333555555555511111117777777
|666|666666|6666|
| | |4444|444|444444444444|4444444| |55555|55555|
| |222222|2222|222| |3333333|333333333|33333| | |7777777
1111111|111|111111|1111|111|111111111111|1111111|111111111|11111|11111|1111111|
1234567|890|123456|7890|123|4
1 -> 1
8 -> 1,6
11 -> 6,2,1
17 -> 6,4,2,1
21 -> 4,2,1
24 -> 4,1
...
- 选择每个列表中最大的元素
- 用相同的最大值连接以下记录
- 创建所有日期条目的表,[日期->ID]
- 对于原始表中的每条记录:
- 选择范围从到的日期
- 如果任何ID值为null或低于当前检查的记录ID,请填写当前ID
- 然后连接-若下一条记录和上一条记录具有相同的ID,则删除下一条记录
- 最后,您可能需要对位进行反规范化,而不是为一个范围提取两个连续记录