Я пытаюсь преобразовать некоторые из моих SQL запросов в LINQ-запросы.
У меня есть следующий запрос SQL, который в основном получает точки JourneyStart, Journey End и объединяется со всеми промежуточными точками для расчета пройденного расстояния.
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
При преобразовании в запрос LINQ я получил следующее:
public static void Main()
{
var VehicleMonitoringLogs = VehicleMonitoringLog.GetData();
DateTime _startDate = new DateTime(2000, 02, 01);
DateTime _endDate = new DateTime(2020, 02, 02).AddDays(1).AddTicks(-1);
var assetIds = new int[] { 2 };
// end of params
var locationLogsWithDistance =
// first get all location logs based on parameters criteria
from locationLog in VehicleMonitoringLogs
.Where(s =>
assetIds.Contains(s.AssetId)
&& s.LogDateTime >= _startDate
&& s.LogDateTime <= _endDate
)
.OrderBy(t => t.LogDateTime)
// then for each location log in the above criteria get it's previous position - this is to calculate distance between current position and previous - because a vehicle does not move in a straight line so we need this.
let prevPosition = VehicleMonitoringLogs
.Where(s => s.AssetId == locationLog.AssetId
&& s.LogDateTime >= _startDate
&& s.LogDateTime <= _endDate
&& s.LogDateTime < locationLog.LogDateTime
&& s.LogId != locationLog.LogId)
.OrderByDescending(s => s.LogDateTime)
.FirstOrDefault()
orderby locationLog.LogDateTime
select new
{
AssetId = locationLog.AssetId,
VehicleMonitoringId = locationLog.LogId,
EventCode = locationLog.EventCode,
LogDateTimeUTC = locationLog.LogDateTime,
// calculate distance from previous point and convert from meters to KM
DistanceFromPreviousPoint = Math.Round((prevPosition != null ? locationLog.GeoLocationPoint.Distance(prevPosition.GeoLocationPoint) ?? 0 : default(double)) * .001, 2)
};
// now once we have a list of location logs with distance from previous record we can start calculating the logic for Journey's.
var journeyLogs =
// get all journey start events (Ignition On or EventCode == 1)
from journeyStart in locationLogsWithDistance.Where(s => s.EventCode == 1)
// get the corresponding End Event (Ignition Off or EventCode == 2) - so the first Ignition Off after the existing Ignition On Event
from journeyEnd in locationLogsWithDistance.Where(s =>
s.AssetId == journeyStart.AssetId
&& s.EventCode == 2
&& s.LogDateTimeUTC >= journeyStart.LogDateTimeUTC)
.OrderBy(t => t.LogDateTimeUTC)
.Take(1)
select new
{
AssetId = journeyStart.AssetId,
JourneyStartId = journeyStart.VehicleMonitoringId,
JourneyStartUtc = journeyStart.LogDateTimeUTC,
JourneyEndId = journeyEnd.VehicleMonitoringId,
JourneyEndUtc = journeyEnd.LogDateTimeUTC,
// finally the Distance Travelled is SUM of all distances between each location point between Journey Start and Journey End Events
DistanceTravelled = locationLogsWithDistance
.Where(s =>
s.LogDateTimeUTC >= journeyStart.LogDateTimeUTC &&
s.LogDateTimeUTC <= journeyEnd.LogDateTimeUTC &&
s.AssetId == journeyStart.AssetId)
.Sum(r => r.DistanceFromPreviousPoint)
};
foreach (var j in journeyLogs)
{
Console.WriteLine("{0} | {1} | {2} | {3}", j.AssetId, j.DistanceTravelled, j.JourneyStartUtc, j.JourneyEndUtc);
}
Console.WriteLine("Complete.....");
Console.ReadKey();
}
Результат правильный и ожидаемый, но SQL запрос выполняет новые SELECT
, а затем SUM
журналов местоположения. Поэтому я хотел бы преобразовать эту часть в оператор group by
.
DistanceTravelled = locationLogsWithDistance
.Where(s =>
s.LogDateTimeUTC >= journeyStart.LogDateTimeUTC &&
s.LogDateTimeUTC <= journeyEnd.LogDateTimeUTC &&
s.AssetId == journeyStart.AssetId)
.Sum(r => r.DistanceFromPreviousPoint)
Лог c будет группироваться по journeyStart.AssetId, journeyStart.LogDateTimeUTC
, а затем СУММИРОВАТЬ DistanceFromPreviousPoint
. Но я не могу точно определить способ или синтаксис для этого.
ОБРАЗЕЦ ДАННЫХ И ЗАПРОС
DO TNET FIDDLE И C# ДАННЫЕ