这个问题是我another question的第二部分,但这一次集中在LINQ-to-SQL上。
我有2个表,一个包含仪表ID,另一个包含第一个表中某些仪表的测量值。这是表结构:
MeterConfig :
读数:
我需要从每个仪表的给定时间段获取最后一次读数(及其日期)以及仪表编号。上次读取仪表的日期可能各不相同。有些米可能在那个时期没有任何读数;在这种情况下,必须忽略它们。
我设法做到了这一点,但我对我的表现并不感到激动。有没有更有效的方法,而不必两次做几乎相同的子查询?
(from cfg in MeterConfigs
join r in Readings on cfg.MeterID equals r.MeterID
where
r.Date >= startDate && r.Date <= endDate
select new
{
Number = cfg.MeterNumber,
ReadingDate =
(
from r in Readings
where cfg.MeterID == r.MeterID && r.Date >= startDate && r.Date <= endDate
orderby r.Date descending
select r.Date
).Take(1),
Value =
(
from r in Readings
where cfg.MeterID == r.MeterID && r.Date >= startDate && r.Date <= endDate
orderby r.Date descending
select r.Value
).Take(1)
})
.GroupBy(x => x.Number)
.Select(x => x.First());
答案 0 :(得分:3)
以下是一种可能的解决方案:
MeterConfigs
.Join(
Readings.Where(x => x.Date >= startDate && x.Date <= endDate),
x => x.MeterID,
x => x.MeterID,
(o,i) => new { MeterNumber = o.MeterNumber, Date = i.Date, Value = i.Value }
).GroupBy(x => x.MeterNumber)
.Select(x => {
var lastReading = x.OrderByDescending(y => y.Date).FirstOrDefault();
return new {
Number = x.Key,
ReadingDate = lastReading.Date,
Value = lastReading.Value
};
}
)
如果安装了linqpad,您可以使用我创建的一些虚拟数据将此查询的结果与当前解决方案进行比较:
var MeterConfigs = new [] {
new { MeterID = 1, MeterNumber = "123", Type = "foo" },
new { MeterID = 2, MeterNumber = "456", Type = "bar" },
new { MeterID = 3, MeterNumber = "789", Type = "foo" },
new { MeterID = 4, MeterNumber = "101", Type = "bar" },
};
var Readings = new [] {
new { MeterID = 1, Date = new DateTime(2010, 10, 21), Value = 12.3 },
new { MeterID = 1, Date = new DateTime(2010, 10, 20), Value = 4.3 },
new { MeterID = 1, Date = new DateTime(2010, 10, 19), Value = 56.2 },
new { MeterID = 1, Date = new DateTime(2010, 10, 5), Value = 1.4 },
new { MeterID = 2, Date = new DateTime(2010, 10, 20), Value = 8.2 },
new { MeterID = 3, Date = new DateTime(2010, 10, 21), Value = 34.7 },
new { MeterID = 3, Date = new DateTime(2010, 10, 20), Value = 2.9 },
};
var startDate = new DateTime(2010, 10, 1);
var endDate = new DateTime(2010, 10, 21);
MeterConfigs
.Join(
Readings.Where(x => x.Date >= startDate && x.Date <= endDate),
x => x.MeterID,
x => x.MeterID,
(o,i) => new { MeterNumber = o.MeterNumber, Date = i.Date, Value = i.Value }
).GroupBy(x => x.MeterNumber)
.Select(x => {
var lastReading = x.OrderByDescending(y => y.Date).FirstOrDefault();
return new {
Number = x.Key,
ReadingDate = lastReading.Date,
Value = lastReading.Value
};
}
).Dump();
(from cfg in MeterConfigs
join r in Readings on cfg.MeterID equals r.MeterID
where
r.Date >= startDate && r.Date <= endDate
select new
{
Number = cfg.MeterNumber,
ReadingDate =
(
from r2 in Readings
where cfg.MeterID == r2.MeterID && r2.Date >= startDate && r2.Date <= endDate
orderby r2.Date descending
select r2.Date
).Take(1),
Value =
(
from r2 in Readings
where cfg.MeterID == r2.MeterID && r2.Date >= startDate && r2.Date <= endDate
orderby r2.Date descending
select r2.Value
).Take(1)
})
.GroupBy(x => x.Number)
.Select(x => x.First()).Dump();
返回:
Number ReadingDate Value
123 10/21/2010 12:00:00 AM 12.3
456 10/20/2010 12:00:00 AM 8.2
789 10/21/2010 12:00:00 AM 34.7
对战:
Number ReadingDate Value
123 IEnumerable<DateTime> (1 item) IEnumerable<Double> (1 item)
10/21/2010 12:00:00 AM 12.3
456 IEnumerable<DateTime> (1 item) IEnumerable<Double> (1 item)
10/20/2010 12:00:00 AM 8.2
789 IEnumerable<DateTime> (1 item) IEnumerable<Double> (1 item)
10/21/2010 12:00:00 AM 34.7