C# LINQ-将select sum转换为groupby sum
我正在尝试将一些SQL查询转换为LINQ查询 我有下面的SQL查询-它基本上获取旅程起点、旅程终点,并与其间的所有点连接以计算旅行距离C# LINQ-将select sum转换为groupby sum,c#,linq,C#,Linq,我正在尝试将一些SQL查询转换为LINQ查询 我有下面的SQL查询-它基本上获取旅程起点、旅程终点,并与其间的所有点连接以计算旅行距离 DECLARE @utcStartDate DateTime2 = '2017-02-01 00:00:00.0000000' DECLARE @utcEndDate DateTime2 = '2017-02-02 23:59:59.9999999' DECLARE @assetId INT = 4019 SELECT AssetId ,[
DECLARE @utcStartDate DateTime2 = '2017-02-01 00:00:00.0000000'
DECLARE @utcEndDate DateTime2 = '2017-02-02 23:59:59.9999999'
DECLARE @assetId INT = 4019
SELECT
AssetId
,[Event]
,StartDateTime
,EndDateTime
,JourneyDistance = ROUND(SUM(DistanceCoveredK), 3, 2)
,TotalJourneyTime = JourneyTime
FROM (
SELECT
AssetId = ignOn.iAssetId
,Startlogid = ignOn.iVehicleMonitoringId
,StartDateTime = ignOn.dtUTCDateTime
,Endlogid = ignOff.iVehicleMonitoringId
,EndDateTime = ignOff.dtUTCDateTime
,[Event] = ignOff.eEventCode
,DistanceCoveredK = p.sptGeoLocaitonPoint.STDistance(
LEAD(p.sptGeoLocaitonPoint) OVER(PARTITION BY ignOn.iAssetId, ignOn.dtUTCDateTime ORDER BY p.dtUTCDateTime)) * 0.001
,JourneyTime = DATEDIFF(SECOND, ignOn.dtUTCDateTime, ignOff.dtUTCDateTime)
FROM VehicleMonitoringLog ignOn
CROSS APPLY (
SELECT top(1) iVehicleMonitoringId, eEventCode, dtUTCDateTime, sptGeoLocaitonPoint
FROM VehicleMonitoringLog WHERE
iAssetId = ignOn.iAssetId AND dtUTCDateTime > ignOn.dtUTCDateTime AND eEventCode = 2
ORDER by dtUTCDateTime
) ignOff
INNER JOIN VehicleMonitoringLog p ON p.iAssetId = ignOn.iAssetId AND p.dtUTCDateTime >= ignOn.dtUTCDateTime AND p.dtUTCDateTime <= ignOff.dtUTCDateTime
WHERE
ignOn.dtUTCDateTime > @utcStartDate AND ignOn.dtUTCDateTime < @utcEndDate
AND ignOn.iAssetId = @assetId
AND ignOn.eEventCode = 1
) g
GROUP BY AssetId, [Event], StartDateTime, EndDateTime, JourneyTime
逻辑是按journeyStart.AssetId、journeyStart.LogDateTimeUTC分组,然后将距离与上一个点相加。但我不能确切地确定一种方法或语法来做到这一点
SQL是一种非常有限的编程语言,c语言要好得多。不要试图在c中直接使用SQL语法。寻找更好的方法。我假设每个旅程都以事件1开始,以事件2结束。请参阅下面的代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DateTime utcStartDate = DateTime.Parse("2017-02-01 00:00:00.0000000");
DateTime utcEndDate = utcStartDate.AddDays(2);
int assetId = 4019;
List<VehicleMonitoringLogs> logs = new List<VehicleMonitoringLogs>();
List<VehicleMonitoringLogs> idDates = logs.Where(x => (x.iAssetId == assetId) && (x.dtUTCDateTime > utcStartDate) && (x.dtUTCDateTime < utcEndDate))
.OrderByDescending(x => x.dtUTCDateTime)
.ToList();
var eventOns = idDates.Where(x => x.eEventCode == 2).Select((x,i) => new {endJourney = x, index = i}).ToList();
var eventOffs = idDates.Where(x => x.eEventCode == 1).Select((x, i) => new { startJourney = x, index = i }).ToList();
var results = (from _on in eventOns
join off in eventOffs on _on.index equals off.index
select new { sptGeoLocaitonPoint = .001M * (_on.endJourney.sptGeoLocaitonPoint - off.startJourney.sptGeoLocaitonPoint), JourneyTime = _on.endJourney.dtUTCDateTime - off.startJourney.dtUTCDateTime}
).ToList();
}
}
public class VehicleMonitoringLogs
{
public int iAssetId { get; set; }
public string iVehicleMonitoringId { get; set; }
public DateTime dtUTCDateTime { get; set; }
public int eEventCode { get; set; }
public decimal sptGeoLocaitonPoint { get; set; }
}
}
因此,我们花了一点时间才弄清楚您到底在做什么,但完整的数据、类和代码集使之成为可能 我最后编写了两个帮助函数来实现这一点
double GetDistance(VehicleMonitoringLog log0, VehicleMonitoringLog log1) =>
(log0.GeoLocationPoint.Distance(log1.GeoLocationPoint) ?? 0.0) * .001;
这只是一个计算距离的函数
下面是更复杂的一个:
IEnumerable<IEnumerable<T>> Partition<T>(IEnumerable<T> source, Func<T, bool> open, Func<T, bool> close)
{
IEnumerable<T> Output(IEnumerator<T> enumerator)
{
yield return enumerator.Current;
while (enumerator.MoveNext())
{
yield return enumerator.Current;
if (close(enumerator.Current))
{
yield break;
}
}
}
var e = source.GetEnumerator();
while (e.MoveNext())
{
if (open(e.Current))
{
yield return Output(e).ToArray();
}
}
}
我得到的输出与您的匹配。.GroupByx=>x.AssetId.Selectx=>x.Sumy=>y.DistanceFromPreviousPoint.ToList。groupby创建二维数组[键,列表]因此,您需要一个select通过键进行枚举,然后一个select通过对象进行枚举。@jdweng我想到了这一点,但当时我没有journeyStart.LogDateTimeUTC,因为我需要每个旅程的距离,所以AssetId和journeyStart.LogDateTimeUTCY的组您的查询非常复杂。对于我来说,在没有数据输入和预期输出的情况下,很难将其重构成可以工作的东西。@Enigmativity我已经在LINQ查询中添加了一些注释,并包含了一个指向SQL查询和示例数据的链接-如果您运行该查询,您应该可以在SQL中看到预期的结果。希望如此helps@DawoodAwan-能否提供类定义和数据作为有效的C代码?我不会尝试构建一个数据库,查询它,映射到一个未知的对象模型,然后运行查询。谢谢你的详细回答-Zip对我来说是新的,所以今天我了解到了这一点。最后一个问题-配分函数是否可以与IQueryable配合使用?我真的不想在做所有这些计算之前把清单具体化。更不用说意识到答案——IQueryable是IEnumerable,所以应该是IEnumerablework@DawoodAwan-不,那不是真的。将IQueryable强制转换为IEnumerable,然后迭代集合将导致查询运行。在某种程度上,它会运行—我将尝试使其成为L2E—因为将这么多数据加载到内存中对我来说不起作用。
double GetDistance(VehicleMonitoringLog log0, VehicleMonitoringLog log1) =>
(log0.GeoLocationPoint.Distance(log1.GeoLocationPoint) ?? 0.0) * .001;
IEnumerable<IEnumerable<T>> Partition<T>(IEnumerable<T> source, Func<T, bool> open, Func<T, bool> close)
{
IEnumerable<T> Output(IEnumerator<T> enumerator)
{
yield return enumerator.Current;
while (enumerator.MoveNext())
{
yield return enumerator.Current;
if (close(enumerator.Current))
{
yield break;
}
}
}
var e = source.GetEnumerator();
while (e.MoveNext())
{
if (open(e.Current))
{
yield return Output(e).ToArray();
}
}
}
var journeyLogs =
from s in VehicleMonitoringLogs
where assetIds.Contains(s.AssetId)
where s.LogDateTime >= _startDate
where s.LogDateTime <= _endDate
orderby s.LogDateTime
group s by s.AssetId into gs
from partition in Partition(gs, t => t.EventCode == 1, t => t.EventCode == 2)
let first = partition.First()
where first.EventCode == 1 // ensures we start with a 1
let last = partition.Last()
where last.EventCode == 2 // ensures we end with a 2
select new
{
AssetId = first.AssetId,
JourneyStartId = first.LogId,
JourneyStartUtc = first.LogDateTime,
JourneyEndId = last.LogId,
JourneyEndUtc = last.LogDateTime,
DistanceTravelled = partition.Skip(1).Zip(partition, (p1, p0) => GetDistance(p0, p1)).Sum()
};