Выберите последнее значение за месяц для всех заданных идентификаторов, используя LINQ - PullRequest
1 голос
/ 21 октября 2010

Этот вопрос является второй частью другого вопроса моего, но на этот раз сосредоточенного на LINQ-to-SQL.

У меня есть 2 таблицы, одна из которых содержит идентификаторы счетчиков, а другая содержит измерения для некоторых счетчиков в первой таблице. Это структура таблицы:

MeterConfig

  • MeterID (int)
  • MeterNumber (символ [16])
  • Тип (символ [25])

Показания

  • MeterID (int)
  • Дата (datetime)
  • Значение (числовое (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 Ответ

3 голосов
/ 21 октября 2010

Вот одно из возможных решений:

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

Versus:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...