C# LINQ-将select sum转换为groupby sum

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 ,[

我正在尝试将一些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
    ,[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()
    };