Целостность данных Linq to SQL с группировкой

Некоторое время назад я задал вопрос SQL Server: как поддерживать целостность данных с помощью агрегатных функций с group by? Я получил отличный ответ, но теперь проблема снова возникла, на этот раз с Linq to SQL, а не с простым SQL.

Предыстория: у меня есть таблица с данными GPS, которая выглядит так:

GPS_id, user_id, широта, долгота, server_time, device_time

Я использовал следующий запрос linq, чтобы получить самую последнюю запись gps для определенного набора пользователей:

var query =
    from gps in db.gps_data
    where (from u in db.users
        select u.user_id).Contains(gps.user_id)
    group gps by gps.user_id into groupedGPS
    select groupedGPS;

Затем я прокрутил его так, но мне пришлось сначала заказать его, чтобы правильно получить «самую последнюю запись».

foreach (var gpsItem in query) {
    var ordered = gpsItem.OrderByDescending(g => g.device_time);
    list.Add(ordered.First());
}

Это дало мне то, что мне было нужно, но в любое время у меня есть 100+ пользователей с 500+ записями gps (и все они были доступны таким образом), поэтому этот код занимал 10+ секунд, что я считал неприемлемым.

Затем я изменил его на следующий

var query =
    from gps in db.gps_data
    where (from u in db.users
        select u.user_id).Contains(gps.user_id)
    group gps by gps.user_id into groupedGPS
    select new 
    {
        GPS_id = groupedGPS.Max(x => x.GPS_id),
        user_id = groupedGPS.Max(x => x.user_id),
        latitude = groupedGPS.Max(x => x.latitude),
        longitude = groupedGPS.Max(x => x.longitude),
        server_time = groupedGPS.Max(x => x.server_time),
        device_time = groupedGPS.Max(x => x.device_time) 
    };

Этот запрос действительно казался более быстрым, потому что, насколько я понимаю, все ненужные данные никогда не загружаются в память. Однако, как и в моем первоначальном вопросе несколько месяцев назад, я таким образом потерял целостность данных. Нет гарантии, что я вижу самую последнюю запись, только максимальное значение для всех полей в группе. Это не влияет на большинство полей, но широта и долгота почти всегда неверны, так как это просто max() записи, найденные в группировке, а не самые последние.

Как мне обойти эту проблему? Я понимаю, что у меня есть первое решение, которое возвращает мне правильные данные, но на это уходит слишком много времени.

Спасибо за помощь!


person Mansfield    schedule 18.09.2012    source источник


Ответы (2)


Насколько я понимаю ваш вопрос (новейшая запись для каждого идентификатора пользователя), похоже, что это будет делать то, что вы ищете;

var q =
    from gps in db.gps_data
    where (from gps2 in db.gps_data
           group gps2 by gps2.user_id
           into g
           select new {a = g.Key, b = g.Max(f => f.server_time)})
           .Contains(new {a = gps.user_id, b = gps.server_time})
    select gps;

Это может дать вам дубликаты, если у пользователя есть несколько показаний одновременно, я предполагаю, что это не так.

person Joachim Isaksson    schedule 18.09.2012
comment
РЕДАКТИРОВАТЬ: Неважно - похоже, он работает нормально. Позвольте мне еще немного поиграть с этим ... - person Mansfield; 18.09.2012
comment
И он отлично работает - также в среднем 440 мс вместо 10 с. Благодаря тонну! - person Mansfield; 18.09.2012

Следующее должно работать, но будет выполнять несколько подвыборов, поэтому вам нужно будет проверить свою производительность и убедиться, что встроенный FirstOrDefault работает с вашим поставщиком LINQ:

var query =
    from u in db.users
    select new
    {
        u.user_id,
        latestGPS = (db.gps_data
                    where g.user_id == u.user_id
                    orderby g.server_time descending
                    select g).FirstOrDefault()
    };
person Jim Wooley    schedule 18.09.2012