2010-11-27 6 views
6

J'ai une table de base de données avec des lignes qui contiennent chacune un index séquentiel. Je veux sélectionner des groupes de rangées consécutives en fonction de cette colonne d'index. Par exemple, si j'avais des lignes avec les valeurs d'index suivantes:Sélection des entrées consécutives avec LINQ aux entités

1 
3 
4 
5 
7 
9 
10 
11 
12 
15 
16 

et je voulais sélectionner tous les groupes avec 3 indices consécutifs (ce nombre varie). Je recevrais les groupes suivants:

3, 4, 5 

9, 10, 11 

10, 11, 12 

Fondamentalement, je suis en train de réaliser quelque chose de similaire à la question posée ici:

selecting consecutive numbers using SQL query

Cependant, je veux mettre en œuvre ce avec LINQ to Entities , pas de SQL réel. Je préférerais également ne pas utiliser de procédures stockées, et je ne veux pas faire d'approche ToList/looping.

Éditer: Les groupes avec plus que les éléments consécutifs demandés n'ont pas nécessairement besoin d'être séparés. c'est-à-dire dans l'exemple précédent, un résultat de 9, 10, 11, 12 serait également acceptable.

Répondre

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

Il génère le code 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]) 

suivant Il pourrait être encore mieux d'utiliser une jointure interne comme celui-ci

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

mais quand je compare les requêtes résultant en studio de gestion qu'ils génèrent le même plan d'exécution et prenez exactement le même temps pour l'exécuter. Je n'ai que cet ensemble de données limité, vous pourriez comparer la performance sur votre jeu de données s'il est plus grand et choisir le meilleur s'ils diffèrent.

+0

Merci pour la réponse. Cependant, cela semble ne fonctionner que pour 3 lignes. J'ai besoin du nombre pour varier; Je pourrais avoir besoin de deux rangées consécutives, ou j'en aurais besoin de 20. Y a-t-il un moyen d'y parvenir? – knoia

0

Le code suivant trouvera chaque "racine".

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

Il ne trouvera le premier élément de chaque groupe de sorte que vous aurez soit à modifier la requête remeber les autres ou vous pouvez faire une requête basée sur le fait que vous avez les racines et de ceux que vous savoir quels index obtenir. Je suis désolé de ne pas pouvoir m'en sortir avant que je ne parte, mais peut-être que ça aide un peu.

PS. si count est 2 comme dans ce cas cela signifie si trouve des groupes de 3.

+0

Cela semble fonctionner.Mais j'ai une autre préoccupation avec cette approche: faire autant de jointures serait-il mauvais pour la performance? Dans le sujet, j'ai lié à la première réponse seulement semblait faire une seule jointure. – knoia

+0

J'ai testé cela avec un nombre de 6 et terminé avec environ 130 jointures. – knoia

1

Donc je pense que je suis venu avec une assez bonne solution modélisée après la réponse de Brian dans le sujet auquel je suis lié.

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; 

Modifiez 3 le nombre de lignes consécutives souhaitées. Cela vous donne le premier index de chaque groupe de lignes consécutives. Appliqué à l'exemple d'origine que j'ai fourni, vous obtiendrez:

3 
9 
10 
1

Je pense que cela pourrait fonctionner assez efficacement (C# bien):

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

Ceci devrait fonctionner dans Linq to Objects, mais pas Linq to Entities. – StriplingWarrior

Questions connexes