如何在 EF 中创建按时间降序排列的连接
How to create a descending time ordered join in EF
我正在尝试提高代码和一组 EF 查询的性能。涉及三个表。 Devices
是具有设备特定属性的设备列表。 Monitors
是可以为设备采样的单个数据元素的列表。 MonitorSamples
存储给定时间戳的每个监视器的单个样本。我正在尝试 return 每个设备的每个监视器的最新数据示例列表。
以下是 returning 该数据的未优化方法。我想将尽可能多的责任推给数据库,而不是创建那么多背靠背请求。如果只优化一个订单,其中查询 return 是每个设备的最新 MonitorSample
,那将减少请求的数量,但如果我可以将其优化为单个数据库查询,那将是最好的.我考虑过构建一个视图来表示该数据,但如果我可以在 linq 或标准查询中完成,那会好得多。由于时间戳上的索引,returning 单个 MonitorSample
的性能很好,但是当请求很多时,性能开始下降很多。
我正在尝试找出创建包含我需要的所有信息的联接的最佳方法,并将其 select 放入我可以使用的新对象中。我在为连接中包含的每个监视器按时间戳排序数据时遇到问题。
public class MonitorSampleData
{
public string Name;
public DateTime Time;
public int Sequence;
public string Type;
public string Unit;
public string Value;
}
public class DeviceData
{
public string DeviceName;
public string DeviceType;
public ICollection<MonitorSampleData> Monitors;
}
public partial class Device
{
public int Id;
public string Name;
public string Description;
public string Location;
public int? SampleRate;
public bool? SynchronizedSampling;
public virtual DeviceType Type;
public virtual DeviceGroup Group;
public virtual DeviceState State;
public virtual ICollection<Monitor> Monitors;
}
public partial class Monitor
{
public int Id;
public string Name;
public bool Enabled;
public int Sequence;
public virtual Device Device;
public virtual MonitorUnitType UnitType;
public virtual MonitorDataType DataType;
public virtual ICollection<MonitorSample> Samples;
}
public partial class MonitorSample
{
public int Id;
public System.DateTime Time;
public string Value;
public virtual Monitor Monitor;
}
public ICollection<DeviceData> GetLatestDeviceData()
{
ICollection<DeviceData> data = new List<DeviceData>();
using (var context = new ApplicationDbContext())
{
var devices = context.GetDevices();
foreach (var device in devices)
{
var deviceData = new DeviceData();
deviceData.DeviceName = device.Name;
deviceData.DeviceType = device.Type.ShortName;
foreach (var monitor in device.Monitors)
{
var sample = context.GetLatestDataByMonitor(monitor);
if (sample != null)
{
MonitorSampleData monitorData = new MonitorSampleData();
monitorData.Name = monitor.Name;
monitorData.Time = sample.Time;
monitorData.Sequence = monitor.Sequence;
monitorData.Type = monitor.DataType.Name;
monitorData.Unit = monitor.UnitType.Name;
monitorData.Value = sample.Value;
deviceData.Monitors.Add(monitorData);
}
}
data.Add(deviceData);
}
}
return data;
}
public MonitorSample GetLatestDataByMonitor(Monitor monitor)
{
MonitorSample sample = null;
if (monitor != null)
{
sample = (from s in MonitorSamples
where s.Monitor.Id == monitor.Id
orderby s.Time descending
select s).FirstOrDefault();
}
return sample;
}
听起来像标准投影 LINQ 查询。
利用导航属性,将 foreach
替换为 from
,将 var sample =
替换为 let sample =
,并且不使用自定义方法,而是将所有内容嵌入查询和class 初始化表达式。
这样,将通过单个数据库查询检索结果。
例如
var data =
(from device in context.Devices
select new DeviceData
{
DeviceName = device.Name,
DeviceType = device.Type.ShortName,
Monitors =
(from monitor in device.Monitors
let sample = (from s in monitor.Samples
orderby s.Time descending
select s).FirstOrDefault()
select new MonitorSampleData
{
Name = monitor.Name,
Time = sample.Time,
Sequence = monitor.Sequence,
Type = monitor.DataType.Name,
Unit = monitor.UnitType.Name,
Value = sample.Value
}).ToList()
}).ToList();
而不是
let sample = (from s in monitor.Samples
orderby s.Time descending
select s).FirstOrDefault()
您可以尝试功能等效的构造
from sample in monitor.Samples
.DefaultIfEmpty()
.OrderByDescending(s => s.Time)
.Take(1)
这可能会变得更好 SQL 翻译。
更新: 我错过了 if (sample != null)
检查您的原始代码。所以真正的 LINQ 等价物是删除了 .DefaultIfEmpty()
的第二个构造,这将强制 INNER JOIN
:
from sample in monitor.Samples
.OrderByDescending(s => s.Time)
.Take(1)
我正在尝试提高代码和一组 EF 查询的性能。涉及三个表。 Devices
是具有设备特定属性的设备列表。 Monitors
是可以为设备采样的单个数据元素的列表。 MonitorSamples
存储给定时间戳的每个监视器的单个样本。我正在尝试 return 每个设备的每个监视器的最新数据示例列表。
以下是 returning 该数据的未优化方法。我想将尽可能多的责任推给数据库,而不是创建那么多背靠背请求。如果只优化一个订单,其中查询 return 是每个设备的最新 MonitorSample
,那将减少请求的数量,但如果我可以将其优化为单个数据库查询,那将是最好的.我考虑过构建一个视图来表示该数据,但如果我可以在 linq 或标准查询中完成,那会好得多。由于时间戳上的索引,returning 单个 MonitorSample
的性能很好,但是当请求很多时,性能开始下降很多。
我正在尝试找出创建包含我需要的所有信息的联接的最佳方法,并将其 select 放入我可以使用的新对象中。我在为连接中包含的每个监视器按时间戳排序数据时遇到问题。
public class MonitorSampleData
{
public string Name;
public DateTime Time;
public int Sequence;
public string Type;
public string Unit;
public string Value;
}
public class DeviceData
{
public string DeviceName;
public string DeviceType;
public ICollection<MonitorSampleData> Monitors;
}
public partial class Device
{
public int Id;
public string Name;
public string Description;
public string Location;
public int? SampleRate;
public bool? SynchronizedSampling;
public virtual DeviceType Type;
public virtual DeviceGroup Group;
public virtual DeviceState State;
public virtual ICollection<Monitor> Monitors;
}
public partial class Monitor
{
public int Id;
public string Name;
public bool Enabled;
public int Sequence;
public virtual Device Device;
public virtual MonitorUnitType UnitType;
public virtual MonitorDataType DataType;
public virtual ICollection<MonitorSample> Samples;
}
public partial class MonitorSample
{
public int Id;
public System.DateTime Time;
public string Value;
public virtual Monitor Monitor;
}
public ICollection<DeviceData> GetLatestDeviceData()
{
ICollection<DeviceData> data = new List<DeviceData>();
using (var context = new ApplicationDbContext())
{
var devices = context.GetDevices();
foreach (var device in devices)
{
var deviceData = new DeviceData();
deviceData.DeviceName = device.Name;
deviceData.DeviceType = device.Type.ShortName;
foreach (var monitor in device.Monitors)
{
var sample = context.GetLatestDataByMonitor(monitor);
if (sample != null)
{
MonitorSampleData monitorData = new MonitorSampleData();
monitorData.Name = monitor.Name;
monitorData.Time = sample.Time;
monitorData.Sequence = monitor.Sequence;
monitorData.Type = monitor.DataType.Name;
monitorData.Unit = monitor.UnitType.Name;
monitorData.Value = sample.Value;
deviceData.Monitors.Add(monitorData);
}
}
data.Add(deviceData);
}
}
return data;
}
public MonitorSample GetLatestDataByMonitor(Monitor monitor)
{
MonitorSample sample = null;
if (monitor != null)
{
sample = (from s in MonitorSamples
where s.Monitor.Id == monitor.Id
orderby s.Time descending
select s).FirstOrDefault();
}
return sample;
}
听起来像标准投影 LINQ 查询。
利用导航属性,将 foreach
替换为 from
,将 var sample =
替换为 let sample =
,并且不使用自定义方法,而是将所有内容嵌入查询和class 初始化表达式。
这样,将通过单个数据库查询检索结果。
例如
var data =
(from device in context.Devices
select new DeviceData
{
DeviceName = device.Name,
DeviceType = device.Type.ShortName,
Monitors =
(from monitor in device.Monitors
let sample = (from s in monitor.Samples
orderby s.Time descending
select s).FirstOrDefault()
select new MonitorSampleData
{
Name = monitor.Name,
Time = sample.Time,
Sequence = monitor.Sequence,
Type = monitor.DataType.Name,
Unit = monitor.UnitType.Name,
Value = sample.Value
}).ToList()
}).ToList();
而不是
let sample = (from s in monitor.Samples
orderby s.Time descending
select s).FirstOrDefault()
您可以尝试功能等效的构造
from sample in monitor.Samples
.DefaultIfEmpty()
.OrderByDescending(s => s.Time)
.Take(1)
这可能会变得更好 SQL 翻译。
更新: 我错过了 if (sample != null)
检查您的原始代码。所以真正的 LINQ 等价物是删除了 .DefaultIfEmpty()
的第二个构造,这将强制 INNER JOIN
:
from sample in monitor.Samples
.OrderByDescending(s => s.Time)
.Take(1)