2010-11-27 13 views
6

Tengo una tabla de base de datos con las filas que contienen cada uno un índice secuencial. Quiero seleccionar grupos de filas consecutivas basadas en esta columna de índice. Por ejemplo, si tuviera filas con los siguientes valores de índice:Selección de entradas consecutivas con LINQ a Entidades

1 
3 
4 
5 
7 
9 
10 
11 
12 
15 
16 

y quería seleccionar todos los grupos con 3 índices consecutivos (este número puede variar). Me gustaría tener los siguientes grupos:

3, 4, 5 

9, 10, 11 

10, 11, 12 

Básicamente, estoy tratando de lograr algo similar a la pregunta planteada aquí:

selecting consecutive numbers using SQL query

Sin embargo, quiero poner en práctica esto con LINQ a Entidades , no SQL real También preferiría no utilizar procedimientos almacenados, y no quiero hacer ningún tipo de enfoque ToList/looping.

Editar: Los grupos con más de los elementos consecutivos solicitados no necesariamente tienen que ser escindido. es decir, en el ejemplo anterior, un resultado de 9, 10, 11, 12 también sería aceptable.

Respuesta

0
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); 
    } 
} 

Se genera el siguiente código 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]) 

Podría ser incluso mejor utilizar una unión interna como esto

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

pero cuando comparo las consultas resultantes en el estudio de gestión que generan el mismo plan de ejecución y toma exactamente el mismo tiempo para ejecutar. Solo tengo este conjunto de datos limitado, puede comparar el rendimiento en su conjunto de datos si es más grande y elegir el mejor si difieren.

+0

Gracias por la respuesta. Sin embargo, parece que solo funcionará para 3 filas. Necesito el número para variar; Es posible que necesite 2 filas consecutivas, o podría necesitar 20. ¿Hay alguna manera de lograr esto? – knoia

0

El siguiente código se encuentra cada "raíz".

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

Sólo se encontrará el primer elemento de cada grupo por lo que o bien tienen que modificar la consulta a remeber los otros o se puede hacer una consulta basada en el hecho de que tiene las raíces y de las que se saber qué índices obtener Lamento no poder terminar antes de tener que ir, pero tal vez ayuda un poco.

PS. si el recuento es 2, como en este caso significa que si encuentra grupos de 3.

+0

Parece que funcionaría.Pero tengo otra preocupación con este enfoque: ¿hacer tantas uniones sería malo para el rendimiento? En el tema que vinculaba a la respuesta superior solo parecía hacer una sola combinación. – knoia

+0

Probé esto con un conteo de 6 y terminé con alrededor de 130 uniones. – knoia

1

Así que creo que he llegado con una solución bastante buena modelado después de la respuesta de Brian en el tema he vinculado.

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; 

Cambio 3 al número de filas consecutivas que desee. Esto le proporciona el primer índice de cada grupo de filas consecutivas. Aplicado al ejemplo original, que he proporcionado, se llega a:

3 
9 
10 
1

Creo que esto podría funcionar bastante eficiente (C#, aunque):

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

Esto debería funcionar en Linq para objetos, pero no Linq para entidades. – StriplingWarrior