Linq连接按时间戳排序的三个表
Linq join three tables orderd by Timstamp
我有三个table:
Sites:
Id
Timezone (string)
SiteName
Vehicles:
Id
SiteId
Name
Positions:
Id
TimestampLocal (DateTimeOffset)
VehicleId
Data1
Data2
...
Data50
一辆车有多个位置。位置 table 非常大(100 多条记录)
我需要获取每辆车的最后位置(通过时间戳,因为它们可以发送旧数据)及其时区,以便我可以根据时区进行进一步的数据处理。像 {PositionId, VehicleId, Timezone, Data1}
我试过:
var result =
from ot in entities.Positions
join v in entities.Vehicles on ot.VehicleId equals v.Id
join s in entities.Sites on v.SiteId equals s.Id
group ot by ot.VehicleId into grp
select grp.OrderByDescending(g=>g.TimestampLocal).FirstOrDefault();
然后我用以下方法处理数据:
foreach (var rr in result){... update Data1 field ... }
这获取了最后的值,但它确实带来了位置中的所有字段(大量数据)并且没有时区。此外,foreach 部分非常 CPU 密集(因为它可能会带来数据),因为它会在几秒钟内达到 100% CPU。
这如何在 Linq 中完成...并且对于数据库及其传输来说是轻量级的?
检查以下内容,它与您所做的相同,但结果包含 Sites
Timezone
预计的列,它预计在 Join 本身
var result =
S.Join(V,s1=>s1.Id,v1=>v1.SiteId,(s1,v1)=>new {v1.Id,s1.Timezone})
.Join(P,v1=>v1.Id,p1=>p1.VehicleId,(v1,p1)=>new {p1,v1.Timezone})
.GroupBy(g=>g.p1.VehicleId)
.Select(x=>x.OrderByDescending(y=>y.p1.TimestampLocal).FirstOrDefault())
.Select(y=>new {y.p1,y.Timezone});
下面是与您提出的问题相关的要点:
要减少提取的列数,因为您可能不想要所有的位置列,需要完成以下操作:
这一行 - Join(P,v1=>v1.Id,p1=>p1.VehicleId,(v1,p1)=>new {p1,v1.Timezone})
投影字段,这是加入的结果,类似于:
new {p1.Id,p1.TimestampLocal,p1.VehicleId,p1.Data1,p1...,v1.Timezone}
将仅提供投影字段,但随后 GroupBy
将更改为 GroupBy(g=>g.VehicleId)
相同的其他选项是更改 GroupBy 投影,如下所示而不是 Join 语句
GroupBy(g=>g.p1.VehicleId,
g=>new {g.p1.Id,g.p1.TimestampLocal,g.p1.VehicleId,g.p1.Data1,g.p1...,g.Timezone})
现在剩余的部分过程 CPU 密集且使用 100% CPU,可以进行以下优化:
- 每个
foreach
循环迭代是否执行网络 Update
调用,那么它必然会使其成为一个网络密集型过程,因此速度较慢,最好是在内存中进行所有必要的更改然后一次性更新数据库,如果您有数百万条记录 ,这仍然会很密集
即使对百万条记录做同样的事情也永远不是一个好主意,因为那将是一个相当大的网络并且 CPU 密集,从而使其变慢,您的选择是:
将内存分成更小的组件,因为我不确定是否有人需要一次性更新百万条记录,所以让它成为多个更小的更新,这些更新由用户,但对系统资源的负担要小得多。
在内存中引入较小的数据集,使用参数在数据库级别进行过滤,并将修改所需的数据传递到内存中进行更新。
使用如上Linq所示的projection,只引入需要的列,这样会减少整体数据内存占用,势必会有影响。
如果逻辑是各种更新是互斥的,那么在线程安全结构中使用Parallel API进行,这样可以确保高效快速地利用CPU在所有核心中,因此速度更快,尽管它会飙升至 100% CPU 但会达到非并行执行的一小部分
除此之外,请提供具体细节以帮助您了解更多细节,这些是基本建议,没有解决此类优化问题的黄金法则
我有三个table:
Sites:
Id
Timezone (string)
SiteName
Vehicles:
Id
SiteId
Name
Positions:
Id
TimestampLocal (DateTimeOffset)
VehicleId
Data1
Data2
...
Data50
一辆车有多个位置。位置 table 非常大(100 多条记录) 我需要获取每辆车的最后位置(通过时间戳,因为它们可以发送旧数据)及其时区,以便我可以根据时区进行进一步的数据处理。像 {PositionId, VehicleId, Timezone, Data1}
我试过:
var result =
from ot in entities.Positions
join v in entities.Vehicles on ot.VehicleId equals v.Id
join s in entities.Sites on v.SiteId equals s.Id
group ot by ot.VehicleId into grp
select grp.OrderByDescending(g=>g.TimestampLocal).FirstOrDefault();
然后我用以下方法处理数据:
foreach (var rr in result){... update Data1 field ... }
这获取了最后的值,但它确实带来了位置中的所有字段(大量数据)并且没有时区。此外,foreach 部分非常 CPU 密集(因为它可能会带来数据),因为它会在几秒钟内达到 100% CPU。
这如何在 Linq 中完成...并且对于数据库及其传输来说是轻量级的?
检查以下内容,它与您所做的相同,但结果包含 Sites
Timezone
预计的列,它预计在 Join 本身
var result =
S.Join(V,s1=>s1.Id,v1=>v1.SiteId,(s1,v1)=>new {v1.Id,s1.Timezone})
.Join(P,v1=>v1.Id,p1=>p1.VehicleId,(v1,p1)=>new {p1,v1.Timezone})
.GroupBy(g=>g.p1.VehicleId)
.Select(x=>x.OrderByDescending(y=>y.p1.TimestampLocal).FirstOrDefault())
.Select(y=>new {y.p1,y.Timezone});
下面是与您提出的问题相关的要点:
要减少提取的列数,因为您可能不想要所有的位置列,需要完成以下操作:
这一行 -
Join(P,v1=>v1.Id,p1=>p1.VehicleId,(v1,p1)=>new {p1,v1.Timezone})
投影字段,这是加入的结果,类似于:new {p1.Id,p1.TimestampLocal,p1.VehicleId,p1.Data1,p1...,v1.Timezone}
将仅提供投影字段,但随后
GroupBy
将更改为GroupBy(g=>g.VehicleId)
相同的其他选项是更改 GroupBy 投影,如下所示而不是 Join 语句
GroupBy(g=>g.p1.VehicleId, g=>new {g.p1.Id,g.p1.TimestampLocal,g.p1.VehicleId,g.p1.Data1,g.p1...,g.Timezone})
现在剩余的部分过程 CPU 密集且使用 100% CPU,可以进行以下优化:
- 每个
foreach
循环迭代是否执行网络Update
调用,那么它必然会使其成为一个网络密集型过程,因此速度较慢,最好是在内存中进行所有必要的更改然后一次性更新数据库,如果您有数百万条记录 ,这仍然会很密集
- 每个
即使对百万条记录做同样的事情也永远不是一个好主意,因为那将是一个相当大的网络并且 CPU 密集,从而使其变慢,您的选择是:
将内存分成更小的组件,因为我不确定是否有人需要一次性更新百万条记录,所以让它成为多个更小的更新,这些更新由用户,但对系统资源的负担要小得多。
在内存中引入较小的数据集,使用参数在数据库级别进行过滤,并将修改所需的数据传递到内存中进行更新。
使用如上Linq所示的projection,只引入需要的列,这样会减少整体数据内存占用,势必会有影响。
如果逻辑是各种更新是互斥的,那么在线程安全结构中使用Parallel API进行,这样可以确保高效快速地利用CPU在所有核心中,因此速度更快,尽管它会飙升至 100% CPU 但会达到非并行执行的一小部分
除此之外,请提供具体细节以帮助您了解更多细节,这些是基本建议,没有解决此类优化问题的黄金法则