Суммарный запрос Linq по месяцу активности - PullRequest
2 голосов
/ 26 июня 2010

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

В основном у меня есть простая таблица

CREATE TABLE Item
(
    id int not null identity(1,1),
    create datetime not null,
    close datetime --null means not closed yet
);

и я хочу сделать это в течение определенного промежутка времени (скажем, с 01.01.2010 по 01.06.2010), для каждого месяца мне нужно количество элементов, которые были активны в этом месяце. Элемент активен, если он был создан в течение или до этого месяца и не закрыт (т.е. закрыт равен нулю) или был закрыт после этого месяца. Поэтому я перевел это в выражение linq, используя вспомогательный метод:

//just returns the first day of every month inbetween min and max (inclusive)
private IEnumerable<DateTime> EnumerateMonths(DateTime Min, DateTime Max)
{
    var curr = new DateTime(Min.Year, Min.Month, 1);
    var Stop = new DateTime(Max.Year, Max.Month, 1).AddMonths(Max.Day == 1 ? 0 : 1);
    while(curr < Stop)
    {
        yield return curr;
        curr = curr.AddMonths(1);
    }
}

public List<DataPoint> GetBacklogByMonth(DateTime min, DateTime max)
{
    return EnumerateMonths(min, max)
        .Select(m => new DataPoint
                        {
                            Date = m,
                            Count = DB.Items.Where(s => s.Create <= m.AddMonths(1) && (!s.Close.HasValue || s.Close.Value >= m.AddMonths(1)))
                                    .Count()
                            }
            ).ToList();
}

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

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

Есть предложения?

Ответы [ 4 ]

0 голосов
/ 30 июня 2010

Я не хочу отвечать на свой вопрос, но вот что я сделал.

Что мне действительно нужно было сделать, так это сделать левое соединение с таблицей месяцев, затем выполнить группу и рассчитать количество элементов для каждого месяца. обычная группировка по месяцам не сработает, потому что тогда элементы будут учитываться только за один месяц, а не все, для которых они были активны. Поэтому я добавил таблицу «Месяцы», содержащую только даты первого числа месяца, и применил к ней левое соединение. Эту операцию нужно выполнять достаточно часто, чтобы я подумал, что стоит добавить для нее таблицу.

вот окончательный запрос:

        var joins = from m in DB.Months
                    from s in DB.Items
                    let nm = m.month.AddMonths(1)
                    where s.Create < nm && (!s.Close.HasValue || s.Close.Value >= nm) && m.month >= min && m.month <= max
                    select new { d = m.month, id = s.ID };
        var counts = from j in joins
                     group j by j.d into g
                     select new DataPoint { Date = g.Key, Count = g.Key > DateTime.Now ? 0 : g.Count() };

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

0 голосов
/ 27 июня 2010

Один из 101 образцов LINQ - это год выполнения, а затем месяц

http://msdn.microsoft.com/en-us/vcsharp/aa336754.aspx#nested

0 голосов
/ 27 июня 2010

Я бы пошел с тем, что говорит Джей.У меня была похожая ситуация.Выполнение сортировки / запроса в памяти будет работать быстрее, чем многократное попадание в БД.

Если вы заранее знаете, что собираетесь только читать, установите objectContext.Table на MergeOption.NoTracking и выполните итерацию с помощью цикла foreach.

Если вам все еще нужноотслеживание, отсоедините объект от dataContext после его использования

var results = from t in DBContext.table select t where t.criteria=your select criteria
foreach (var result in results)
{
  DoSomething(result);
  DbContext.Detach(result);
}

В качестве альтернативы, если вы не используете отслеживание, вам не нужно отсоединять ваши объекты

0 голосов
/ 26 июня 2010

Просто сначала потяните свои вещи, а затем пролонгируйте месяцы, используя коллекцию в памяти. Я не уверен, что у вас есть правильные критерии для запроса БД, но в основном это будет:

var items = Db.Items.Where(s => s.Create <= min 
    && (!s.Close.HasValue || s.Close.Value >= max)).ToList();

return EnumerateMonths(min, max).Select(m => new DataPoint
    {
        Date = m,
        Count = items.Where(s => s.Create <= m.AddMonths(1) && (!s.Close.HasValue || s.Close.Value >= m.AddMonths(1))).Count()
    }).ToList();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...