Выбор последовательных записей с LINQ to Entities - PullRequest
6 голосов
/ 27 ноября 2010

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

1
3
4
5
7
9
10
11
12
15
16

и я хотел выбрать все группы с 3 последовательными индексами (это число будет меняться). Я бы получил следующие группы:

3, 4, 5

9, 10, 11

10, 11, 12

По сути, я пытаюсь добиться чего-то похожего на вопрос, заданный здесь:

выбор последовательных чисел с помощью SQL-запроса

Однако я хочу реализовать это с помощью LINQ to Entities, а не реального SQL. Я также предпочел бы не использовать хранимые процедуры, и я не хочу использовать какой-либо подход ToList / зацикливания.

Редактировать: Группы с более чем запрошенными последовательными элементами не обязательно должны быть разделены. то есть в предыдущем примере результаты 9, 10, 11, 12 также были бы приемлемыми.

Ответы [ 4 ]

1 голос
/ 13 декабря 2010

Я думаю, что это может работать довольно эффективно (хотя C #):

int[] query = { 1, 3, 4, 5, 7, 9, 10, 11, 12, 15, 16 };
int count = 3;
List<List<int>> numbers = query
   .Where(p => query.Where(q => q >= p && q < p + count).Count() == count)
   .Select(p => Enumerable.Range(p, count).ToList())
   .ToList();
1 голос
/ 28 ноября 2010

Так что я думаю, что придумал довольно хорошее решение, смоделированное после ответа Брайана в теме, на которую я ссылался.

var q = from a in query
        from b in query
        where a.Index < b.Index
        && b.Index < a.Index + 3
        group b by new { a.Index }
            into myGroup
            where myGroup.Count() + 1 == 3
            select myGroup.Key.Index;

Измените 3 на количество последовательных строк, которое вы хотите. Это дает вам первый индекс каждой группы последовательных строк. Применительно к исходному примеру, который я предоставил, вы получите:

3
9
10
0 голосов
/ 28 ноября 2010

Следующий код найдет каждый «корень».

    var query = this.commercialRepository.GetQuery();
    var count = 2;
    for (int i = 0; i < count; i++)
    {
        query = query.Join(query, outer => outer.Index + 1, inner => inner.Index, (outer, inner) => outer);
    }

    var dummy = query.ToList();

Он найдет только первый элемент в каждой группе, поэтому вам придется либо изменить запрос, чтобы запомнить другие, либо вы могли бы сделать запрос, основываясь на том факте, что у вас есть корни и от тех, кого вы знаете, какие индексы нужно получить. Мне жаль, что я не смог закончить это, прежде чем мне пришлось идти, но, возможно, это немного помогает.

PS. если число равно 2, как в этом случае, это означает, что находит группы из 3.

0 голосов
/ 28 ноября 2010
using (var model = new AlbinTestEntities())
{
    var triples = from t1 in model.Numbers
                  from t2 in model.Numbers
                  from t3 in model.Numbers
                  where t1.Number + 1 == t2.Number
                  where t2.Number + 1 == t3.Number
                  select new
                  {
                      t1 = t1.Number,
                      t2 = t2.Number,
                      t3 = t3.Number,
                  };

    foreach (var res in triples)
    {
        Console.WriteLine(res.t1 + ", " + res.t2 + ", " + res.t3);
    }
}

Генерирует следующий SQL

SELECT 
[Extent1].[Number] AS [Number], 
[Extent2].[Number] AS [Number1], 
[Extent3].[Number] AS [Number2]
FROM   [dbo].[Numbers] AS [Extent1]
CROSS JOIN [dbo].[Numbers] AS [Extent2]
CROSS JOIN [dbo].[Numbers] AS [Extent3]
WHERE (([Extent1].[Number] + 1) = [Extent2].[Number]) AND (([Extent2].[Number] + 1) = [Extent3].[Number])

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

using (var model = new AlbinTestEntities())
{
    var triples = from t1 in model.Numbers
                  join t2 in model.Numbers on t1.Number + 1 equals t2.Number
                  join t3 in model.Numbers on t2.Number + 1 equals t3.Number
                  select new
                  {
                      t1 = t1.Number,
                      t2 = t2.Number,
                      t3 = t3.Number,
                  };

    foreach (var res in triples)
    {
        Console.WriteLine(res.t1 + ", " + res.t2 + ", " + res.t3);
    }
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...