2012-03-06 14 views
14

Non riesco a risolvere questo problema con la query LINQ.Gruppo annidato per LINQ

Quindi abbiamo la struttura della tabella come segue: ID || bug_category || bug_name || bug_details || bug_priority

Voglio raggruppare per bug_category prima. Per ogni bug_category, voglio a turno raggruppare per bug__priority.

Quindi, fondamentalmente voglio qualcosa di simile:

bug_category = AUDIO :: No di BUGS -> Critical = 3, Medio = 2 e Low = 7 bug.
bug_category = VIDEO :: No di BUG -> Critico = 5, Medio = 1 e Basso = 9 bug.

La query di seguito riporta le combinazioni tutto uniche di categoria e customer_priority:

(dove RawDataList è semplicemente un elenco di dati che ha la struttura di cui sopra)

 var ProceesedData = from d in RawDataList 
         group d by new { d.bug_category, d.bug_priority } into g 
         select new 
         { 
          g.Key.bug_category, 
          g.Key.bug_priority 
         }; 

La query di seguito restituisce la categoria seguita da un elenco di record in quella categoria:

  var ProceesedData = from d in RawDataList 
         group d by d.bug_category into g 
         select new { g.Key, records = g 
         }; 

Ma io sono un in grado di procedere ulteriormente come ProcessedData (la variabile di ritorno) è un tipo sconosciuto. Qualche idea su questo?

risposta

25

Ho il sospetto che si desidera (nomi modificati per essere più idiomatica):

var query = from bug in RawListData 
      group bug by new { bug.Category, bug.Priority } into grouped 
      select new { 
       Category = grouped.Key.Category, 
       Priority = grouped.Key.Priority, 
       Count = grouped.Count() 
      }; 

Poi:

foreach (var result in query) 
{ 
    Console.WriteLine("{0} - {1} - {2}", 
         result.Category, result.Priority, result.Count); 
} 

In alternativa (ma si veda più avanti):

var query = from bug in RawListData 
      group bug by new bug.Category into grouped 
      select new { 
       Category = grouped.Category, 
       Counts = from bug in grouped 
         group bug by grouped.Priority into g2 
         select new { Priority = g2.Key, Count = g2.Count() } 
      }; 

foreach (var result in query) 
{ 
    Console.WriteLine("{0}: ", result.Category); 
    foreach (var subresult in result.Counts) 
    { 
     Console.WriteLine(" {0}: {1}", subresult.Priority, subresult.Count); 
    } 
} 

EDIT: Come annotato nei commenti, ciò comporterà più query SQL. Per ottenere un simile risultato, ma la struttura più efficiente è possibile utilizzare:

var dbQuery = from bug in RawListData 
       group bug by new { bug.Category, bug.Priority } into grouped 
       select new { 
        Category = grouped.Key.Category, 
        Priority = grouped.Key.Priority, 
        Count = grouped.Count() 
       }; 

var query = dbQuery.ToLookup(result => result.Category, 
          result => new { result.Priority, result.Count }; 


foreach (var result in query) 
{ 
    Console.WriteLine("{0}: ", result.Key); 
    foreach (var subresult in result) 
    { 
     Console.WriteLine(" {0}: {1}", subresult.Priority, subresult.Count); 
    } 
} 
+2

La seconda query creerà per ogni gruppo una query secondaria, quindi penso che una soluzione migliore è il primo seguito da ToLookup per categoria –

+0

@AdrianIftode: l'hai provato? Non l'ho fatto, ma avrei * previsto * l'SQL appropriato da generare per fare tutto in una volta. –

+0

sì, con LinqPad, una query per i gruppi di categorie e altre query X per i gruppi di priorità –

5

Penso che si sta cercando qualcosa di simile:

var processedData = 
     rawData.GroupBy(bugs => bugs.bug_category, 
      (category, elements) => 
      new 
       { 
        Category = category, 
        Bugs = elements.GroupBy(bugs => bugs.bug_priority, 
             (priority, realbugs) => 
             new 
              { 
               Priority = priority, 
               Count = realbugs.Count() 
              }) 
       }); 
    foreach (var data in processedData) 
    { 
     Console.WriteLine(data.Category); 

     foreach (var element in data.Bugs) 
      Console.WriteLine(" " + element.Priority + " = " + element.Count); 
    } 
+0

Grazie per aver fornito la soluzione. Apprezzalo ! – Nandu

+0

@Nandu: Ti rendi conto che questo è fondamentalmente lo stesso della mia seconda query, solo in forma di espressione lambda, giusto? Si noti che subisce le stesse scarse prestazioni (dato che è la stessa query) che Adrian ha sottolineato. –

+2

I rischi di prendere 9 minuti in più rispetto a Jon Skeet per scrivere la risposta;) –

12

Si tratta di un modo più semplice per realizzare raggruppamenti nidificati. L'ho testato per le raccolte di memoria, indipendentemente dal fatto che il particolare provider DB possa gestirle o meno, oppure che sia sconosciuto o meno.

Supponendo tu avessi due proprietà, e ha voluto gruppo sia da Stato e di Nazione:

var grouped = People 
    .GroupBy(l => new { l.State, l.Country})//group by two things 
    .GroupBy(l=> l.Key.Country)//this will become the outer grouping 


foreach(var country in grouped) 
{ 
    foreach(var state in country) 
    { 
    foreach(var personInState in state) 
    { 
     string description = $"Name: {personInState.Name}, State: {state.StateCode}, Country: {country.CountryCode}"; 
     ... 

    } 
    } 
} 
+0

Questa è una risposta difficile da trovare! –

+0

Sì, mi ci è voluto un po 'per capire quando ho avuto bisogno di questo per me stesso, e in realtà torno alla mia risposta quando ho bisogno di farlo. – AaronLS