C# 如何将SQL转换为具有前瞻性的LINQ(lead-like查询)

C# 如何将SQL转换为具有前瞻性的LINQ(lead-like查询),c#,sql,linq,C#,Sql,Linq,我有一个简单的表,如下所示: [Timestamp] DATETIME, [Value] NVARCHAR(50) 所有[Timestamp]值都是唯一的,但[Value]不是唯一的 我想知道每个[值]何时开始和停止。我有下面的T-SQL(SQL 2008),它似乎工作得很好,尽管任何改进建议都将受到赞赏,因为在2012年之前的T-SQL中实现lead操作符是一种很好的模式。 我预计这一产出: Event ID Start Time End Time

我有一个简单的表,如下所示:

[Timestamp] DATETIME,
[Value] NVARCHAR(50)
所有[Timestamp]值都是唯一的,但[Value]不是唯一的

我想知道每个[值]何时开始和停止。我有下面的T-SQL(SQL 2008),它似乎工作得很好,尽管任何改进建议都将受到赞赏,因为在2012年之前的T-SQL中实现lead操作符是一种很好的模式。

我预计这一产出:

Event ID Start Time              End Time                Duration (sec)
======== ======================= ======================= =============
Violet   2013-12-18 20:26:00.000 2013-12-18 20:30:00.000 240
Red      2013-12-18 20:30:00.000 2013-12-18 20:40:00.000 600
Orange   2013-12-18 20:40:00.000 2013-12-18 20:50:00.000 600
Yellow   2013-12-18 20:50:00.000 2013-12-18 21:00:00.000 600
Green    2013-12-18 21:00:00.000 2013-12-18 21:10:00.000 600
Blue     2013-12-18 21:10:00.000 2013-12-18 21:20:00.000 600
Indigo   2013-12-18 21:20:00.000 2013-12-18 21:30:00.000 600
Violet   2013-12-18 21:30:00.000 2013-12-18 21:40:00.000 600
Red      2013-12-18 21:40:00.000 2013-12-18 21:50:00.000 600
Orange   2013-12-18 21:50:00.000 2013-12-18 22:00:00.000 600
Yellow   2013-12-18 22:00:00.000 2013-12-18 22:10:00.000 600
请注意,对于Green的最后一个实例,没有返回任何内容,因为还没有“结束日期”

然而,我真正感兴趣的是能够使用C#中的LINQ对类似的值和时间戳列表执行相同类型的查询。不是针对SQL数据源,而是针对具有值和时间戳属性的POCO对象列表

所以,如果我有一个类似于

我有一个

List<VT> vts
等等。SQL是有效的,因为它查找的下一个值的时间戳与当前值不同。使用Linq是否可行

可以肯定的是,在上述具有连续值的示例中,最终结果应该是:

Red, T1, T4
Yellow, T4, T6
因此,应将连续读数合并为单个读数。查看SQL查询如何查找与前瞻中的当前记录值不同的下一条记录


我编辑了原始数据集示例,以包含重复的连续值。输出不会改变。

您可以使用ZIP语句实现这一点,其中一个元素跳过的序列与第二个输入相同,这就是您的“展望”。但是,它没有考虑到可能存在多个具有相同值的连续事件

var firstList = new List<VT> {
   new VT { Value = "Violet", Timestamp = new DateTime(2013, 12, 18, 20, 26, 0) },
   new VT { Value = "Red", Timestamp = new DateTime(2013, 12, 18, 20, 30, 0) },
   new VT { Value = "Orange", Timestamp = new DateTime(2013, 12, 18, 20, 40, 0) },
   new VT { Value = "Yellow", Timestamp = new DateTime(2013, 12, 18, 20, 50, 0) }
};

var secondList = firstList.Skip(1);

var combined = firstList.Zip(secondList, 
                               (first, second) => new {
                                      EventID = first.Value, 
                                      StartTime = first.Timestamp, 
                                      EndTime = second.Timestamp, 
                                      Duration = (second.Timestamp - first.Timestamp).TotalSeconds});
}
使用无功扩展(RX)可以轻松解决具有相同值的多个连续事件的问题。只需将firstList和secondList的类型替换为
IObservable
。然后,
.Subscribe()
组合以在颜色发生变化时获得流完成事件。调用
DistinctUntilChanged
将确保只传递具有不同颜色的事件

var firstList = new List<VT> {
  new VT { Value = "Violet", Timestamp = new DateTime(2013, 12, 18, 20, 26, 0) },
  new VT { Value = "Violet", Timestamp = new DateTime(2013, 12, 18, 20, 27, 0) },
  new VT { Value = "Violet", Timestamp = new DateTime(2013, 12, 18, 20, 29, 0) },
  new VT { Value = "Red", Timestamp = new DateTime(2013, 12, 18, 20, 30, 0) },
  new VT { Value = "Red", Timestamp = new DateTime(2013, 12, 18, 20, 34, 0) },
  new VT { Value = "Orange", Timestamp = new DateTime(2013, 12, 18, 20, 40, 0) },
  new VT { Value = "Yellow", Timestamp = new DateTime(2013, 12, 18, 20, 50, 0) }
}
.ToObservable().DistinctUntilChanged(vt => vt.Value);

var secondList = firstList.Skip(1);

var combined = firstList.Zip(secondList, (first, second) => new  {
                                                 EventID = first.Value, 
                                                 StartTime = first.Timestamp, 
                                                 EndTime = second.Timestamp, 
                                                 Duration = (second.Timestamp - first.Timestamp).TotalSeconds});

combined.Subscribe();
var firstList=新列表{
新的VT{Value=“Violet”,时间戳=新的日期时间(2013,12,18,20,26,0)},
新的VT{Value=“Violet”,时间戳=新的日期时间(2013,12,18,20,27,0)},
新的VT{Value=“Violet”,时间戳=新的日期时间(2013,12,18,20,29,0)},
新的VT{Value=“Red”,时间戳=新的日期时间(2013,12,18,20,30,0)},
新的VT{Value=“Red”,时间戳=新的日期时间(2013,12,18,20,34,0)},
新的VT{Value=“Orange”,时间戳=新的日期时间(2013,12,18,20,40,0)},
新的VT{Value=“Yellow”,时间戳=新的日期时间(2013,12,18,20,50,0)}
}
.ToObservable().DistinctUntilChanged(vt=>vt.Value);
var secondList=firstList.Skip(1);
var combined=firstList.Zip(secondList,(first,second)=>new{
EventID=first.Value,
StartTime=first.Timestamp,
EndTime=秒。时间戳,
持续时间=(second.Timestamp-first.Timestamp.TotalSeconds});
组合。订阅();
这将返回预期的结果


DistinctUntilChanged
简单地从流中删除根据值相同的所有事件,因此这有效地减少了对上面LINQ示例中输入的输入。

我可以在Skip()中添加Where子句吗子句,它告诉它跳到列表中的下一个元素,该元素的值与第一个列表中元素的值不相同?如果是,那会是什么样子?见我对问题的澄清。我不一定要寻找下一条记录,而是下一条具有不同值的记录。我不知道是否可以使用标准LINQ运算符来实现这一点,但一般问题在这里得到了解决:事实证明,使用反应式扩展,通过添加一个简单的
DistinctUntilChanged
解决了这个问题。我将尝试@okrumnow链接的实现,看看它是否有效。谢谢,非常感谢。这从我的解决方案中删除了8个源代码文件和一个完整的项目,用一个简单的函数替换了所有这些。我喜欢我的一些反应性扩展!
T1, Red 
T2, Red
T3, Red
T4, Yellow
T5, Yellow
T6, Blue
Red, T1, T4
Yellow, T4, T6
var firstList = new List<VT> {
   new VT { Value = "Violet", Timestamp = new DateTime(2013, 12, 18, 20, 26, 0) },
   new VT { Value = "Red", Timestamp = new DateTime(2013, 12, 18, 20, 30, 0) },
   new VT { Value = "Orange", Timestamp = new DateTime(2013, 12, 18, 20, 40, 0) },
   new VT { Value = "Yellow", Timestamp = new DateTime(2013, 12, 18, 20, 50, 0) }
};

var secondList = firstList.Skip(1);

var combined = firstList.Zip(secondList, 
                               (first, second) => new {
                                      EventID = first.Value, 
                                      StartTime = first.Timestamp, 
                                      EndTime = second.Timestamp, 
                                      Duration = (second.Timestamp - first.Timestamp).TotalSeconds});
}
EventID StartTime           EndTime             Duration
Violet  18.12.2013 20:26:00 18.12.2013 20:30:00 240 
Red     18.12.2013 20:30:00 18.12.2013 20:40:00 600 
Orange  18.12.2013 20:40:00 18.12.2013 20:50:00 600 
var firstList = new List<VT> {
  new VT { Value = "Violet", Timestamp = new DateTime(2013, 12, 18, 20, 26, 0) },
  new VT { Value = "Violet", Timestamp = new DateTime(2013, 12, 18, 20, 27, 0) },
  new VT { Value = "Violet", Timestamp = new DateTime(2013, 12, 18, 20, 29, 0) },
  new VT { Value = "Red", Timestamp = new DateTime(2013, 12, 18, 20, 30, 0) },
  new VT { Value = "Red", Timestamp = new DateTime(2013, 12, 18, 20, 34, 0) },
  new VT { Value = "Orange", Timestamp = new DateTime(2013, 12, 18, 20, 40, 0) },
  new VT { Value = "Yellow", Timestamp = new DateTime(2013, 12, 18, 20, 50, 0) }
}
.ToObservable().DistinctUntilChanged(vt => vt.Value);

var secondList = firstList.Skip(1);

var combined = firstList.Zip(secondList, (first, second) => new  {
                                                 EventID = first.Value, 
                                                 StartTime = first.Timestamp, 
                                                 EndTime = second.Timestamp, 
                                                 Duration = (second.Timestamp - first.Timestamp).TotalSeconds});

combined.Subscribe();