SQL或C#:获取所有时间跨度而不重叠

时间:2013-07-09 17:10:19

标签: c# sql-server math time

给定一个包含两个DateTime列(StartTime和EndTime)的表,以及包含可能重叠的数据的行,如何找到每个组合的开始/结束块的单个实例?

例如,给定:

  • 07/01/2013 00:00:00,07 / 01/2013 12:00:00
  • 07/01/2013 06:00:00,07 / 01/2013 18:00:00

我需要一个结果{07/01/2013 00:00:00,07 / 01/2013 18:00:00}。这项工作可以在SQL查询中完成,也可以在给定DataTable的C#中完成,如上所述。

4 个答案:

答案 0 :(得分:1)

我能想到的最简单的方法是在您感兴趣的时间范围内啜饮所有记录,然后根据众所周知的重叠日期公式“合并”记录:

List<Tuple<DateTime, DateTime>> dateRows = GetDateRowsSomehow();
//sorting by start time; you can do this in SQL pretty easily
//necessary to make sure the row most likely to overlap a given row is the next one
dateRows.Sort((a,b) => a.Item1.CompareTo(b.Item1));

for(var i=0; i<dateRows.Count - 1; i++)
    for(var j=i+1, j<dateRows.Count; j++)
        if(dateRows[i].Item1 <= dateRows[j].Item2
            && dateRows[i].Item2 >= dateRows[j].Item1) //overlap
        {
           //keep date row i, with the values of the complete time range of i and j
           dateRows[i].Item1 = dateRows[i].Item1 < dateRows[j].Item1
               ? dateRows[i].Item1
               : dateRows[j].Item1;
           dateRows[i].Item2 = dateRows[i].Item2 > dateRows[j].Item2
               ? dateRows[i].Item2
               : dateRows[j].Item2;
           //remove row j and ensure we don't skip the row after it
           dateRows.RemoveAt(j);
           j--;
        }

此解决方案的WCS是一个零重叠的大结果集,它将按N(N-1)/ 2 = O (N 2 )的顺序执行。最好的情况是线性的(不计算NlogN排序操作或列表中的重复线性移位),当所讨论的所有行彼此重叠时。你不能使用foreach,因为我们正在改变集合的大小。可能有一种更有效的方式(例如,从前到后遍历列表,最小化移位),但这应该是体面的,而且重要的是,干净简洁。

答案 1 :(得分:1)

您可以使用Time Period Library for .NET计算不重叠的时间跨度:

// ----------------------------------------------------------------------
public void TimeSpansWithoutOverlap()
{
  // periods
  ITimePeriodCollection periods = new TimePeriodCollection();
  periods.Add( new TimeRange( 
    new DateTime( 2013, 7, 1, 0, 0, 0 ), 
    new DateTime( 2013, 7, 1, 12, 0, 0 ) ) );
  periods.Add( new TimeRange( 
    new DateTime( 2013, 7, 1, 6, 0, 0 ),
    new DateTime( 2013, 7, 1, 18, 0, 0 ) ) );

  ITimePeriodCollection combinedPeriods = new TimePeriodCombiner<TimeRange>().CombinePeriods( periods );
  foreach ( ITimePeriod combinedPeriod in combinedPeriods )
  {
    Console.WriteLine( "Period: " + combinedPeriod );
  }
} // TimeSpansWithoutOverlap

答案 2 :(得分:0)

作为初学者,我会创建一个c#类并使用DateTime操作来查找重叠。至于重叠算法,只需区分开始和结束时间。

似乎也在这里完成Algorithm to detect overlapping periods

另外 http://www.codeproject.com/Articles/168662/Time-Period-Library-for-NET

答案 3 :(得分:0)

这样的事应该对你有用:

select *
from myTable t
where not exists ( select *
                   from myTable overlap
                   where overlap.partial_key = t.partial_key
                     and overlap.dateTimeFrom <= t.dateTimeThru
                     and overlap.dateTimeThru >= t.dateTimeFrom
                 )

这是一个简单的相关子查询。

相关问题