2009-01-23 50 views
6

Si tengo una tabla con una columna de título y columnas de 3 bits (f1, f2, f3) que contienen 1 o NULL, ¿cómo escribiría el LINQ para devolver el título con el recuento de cada columna de bits que contiene 1? Estoy buscando el equivalente de esta consulta SQL:LINQ COUNT en varias columnas

SELECT title, COUNT(f1), COUNT(f2), COUNT(f3) FROM myTable GROUP BY title 

Estoy buscando la "mejor" manera de hacerlo. La versión que encontré se sumerge en la tabla 4 veces cuando miras el SQL subyacente, por lo que es demasiado lento.

Respuesta

0

Aquí está la solución que se me ocurrió. Tenga en cuenta que está cerca de la solución propuesta por @OdeToCode (pero en la sintaxis VB), con una diferencia importante:

Dim temp = _ 
    (From t In context.MyTable _ 
    Group t.f1, t.f2, t.f3 By t.title Into g = Group _ 
    Select title, g).ToList 

Dim results = _ 
    From t In temp _ 
    Select t.title, _ 
     f1_count = t.g.Count(Function(x) If(x.f1, False)), _ 
     f2_count = t.g.Count(Function(x) If(x.f2, False)), _ 
     f3_count = t.g.Count(Function(x) If(x.f3, False)) 

La primera consulta que hace la agrupación, pero el ToList obtiene los datos agrupados como está desde el servidor . La eliminación del conteo aquí evita que la declaración SQL resultante produzca sub-SELECT para cada conteo. Hago el recuento en la segunda consulta localmente.

Esto funciona porque sé que la primera consulta devolverá un número manejable de filas. Si devolviera millones de filas, probablemente tendría que ir en otra dirección.

+2

¡No lo use para grandes conjuntos de datos! No copie y pegue esto en su aplicación. Debería haber una gran advertencia en esta respuesta. –

2

Creo que aquí es donde se cae LINQ. Si desea utilizar eficientemente el SQL, si desea un buen código, use LINQ.

Siempre puede ejecutar la consulta directamente, dado que ya conoce el SQL.

class TitleCount { 
    public string Title; 
    public int Count1; 
    public int Count2; 
    public int Count3; 
} 

DataContext dc = new DataContext("Connection string to db"); 

IEnumerable<TitleCount> query = dc.ExecuteQuery<TitleCount>(
    @"SELECT title, 
      COUNT(f1) as Count1, 
      COUNT(f2) as Count2, 
      COUNT(f3) as Count3 
     FROM myTable GROUP BY title"); 
4

Si usted quiere meter a una consulta LINQ y el uso de un tipo anónimo, la consulta podría ser:

var query = 
     from r in ctx.myTable 
     group r by r.title into rgroup 
     select new 
     { 
      Title = rgroup.Key, 
      F1Count = rgroup.Count(rg => rg.f1 == true), 
      F2Count = rgroup.Count(rg => rg.f2 == true), 
      F3Count = rgroup.Count(rg => rg.f3 == true) 
     }; 

El truco consiste en reconocer que desea contar el número de campos verdaderos (se correlaciona como un bool nulo), lo que puedes hacer con el operador de conteo y un predicado. Más información sobre el operador del grupo LINQ aquí: The Standard LINQ Operators

+3

Esa es la solución que se me ocurrió originalmente, pero la consulta resultante utiliza sub-selects para producir cada recuento individual. En una tabla con medio millón de filas (sin índices), esta versión demora 7 segundos, mientras que el SQL apropiado con 3 CUENTAS es instantáneo. – gfrizzle