如何通过LINQ或SQL提取Activity表中相邻状态变更的日志条目
日志相邻状态跳转的SQL/LINQ实现方案
针对Activity表按日写入核查日志、精简留存状态相对相邻记录发生跳转的需求,完全不需要手动在C#中编写循环逐行比对,可直接通过SQL或LINQ原生能力实现,具体实现方式如下:
SQL 实现
主流关系型数据库均支持窗口函数,可通过LAG()(按日期排序取上一条记录值)直接完成相邻状态对比:
SELECT [Date], Found FROM ( SELECT [Date], Found, -- 按日期正序排序,取当前行上1条记录的Found值,无前置记录时返回NULL LAG(Found, 1, NULL) OVER(ORDER BY [Date]) AS PreviousFound FROM Activity ) AS LogSource -- 筛选当前状态与上一条状态不一致的记录 WHERE Found <> PreviousFound ORDER BY [Date]
上述逻辑的筛选结果为:
- 1/3/2021:Found=1,上一条1/2的Found=0,状态发生跳转
- 1/5/2021:Found=0,上一条1/4的Found=1,状态发生跳转
如果需要实现你示例中留存连续状态段末条目的逻辑(即1/2连续0值段的最后一条也留存),可切换为LEAD()(取当前记录的下一条值)实现:
SELECT [Date], Found FROM ( SELECT [Date], Found, -- 取当前行下1条记录的Found值 LEAD(Found, 1, NULL) OVER(ORDER BY [Date]) AS NextFound FROM Activity ) AS LogSource -- 留存当前状态与下一条状态不一致的记录,即连续相同状态段的最后一条 WHERE NextFound <> Found ORDER BY [Date]
上述逻辑的筛选结果为1/2(下一条状态变为1)、1/4(下一条状态变为0),与你给出的示例结果仅存在段首/段末留存的规则差异,可根据实际业务需要调整对比方向。
C# LINQ 实现
如果是在内存中处理已查询出的日志集合,可直接通过LINQ能力完成相邻对比,无需手写循环逻辑:
// 先按日期正序排序列表 var sortedLogs = activityLogs.OrderBy(log => log.Date).ToList(); // 筛选状态与上一条不一致的条目 var changedLogs = sortedLogs .Where((log, index) => // 跳过第一条无前置记录的日志 index > 0 && log.Found != sortedLogs[index - 1].Found ) .ToList();
如果不想依赖索引访问列表,也可通过Zip方法实现滑动窗口对比:
var changedLogs = sortedLogs // 当前序列从第1条开始,前置序列从第2条开始,逐对匹配 .Zip(sortedLogs.Skip(1), (current, prev) => new { current.Date, current.Found, IsChanged = current.Found != prev.Found }) .Where(x => x.IsChanged) .Select(x => new ActivityLog { Date = x.Date, Found = x.Found }) .ToList();
内容的提问来源于stack exchange,提问作者TheWebGuy
相关产品推荐
相关产品推荐

