使用LINQ为所有给定ID选择一个月中的最后一个值

时间:2010-10-21 12:53:10

标签: linq linq-to-sql

这个问题是我another question的第二部分,但这一次集中在LINQ-to-SQL上。

我有2个表,一个包含仪表ID,另一个包含第一个表中某些仪表的测量值。这是表结构:

MeterConfig

  • MeterID(int)
  • MeterNumber(char [16])
  • 类型(字符[25])

读数

  • MeterID(int)
  • 日期(日期时间)
  • 值(数字(18,6))

我需要从每个仪表的给定时间段获取最后一次读数(及其日期)以及仪表编号。上次读取仪表的日期可能各不相同。有些米可能在那个时期没有任何读数;在这种情况下,必须忽略它们。

我设法做到了这一点,但我对我的表现并不感到激动。有没有更有效的方法,而不必两次做几乎相同的子查询?

(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());

1 个答案:

答案 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