none
Query con SUM, CASE, HAVING en LINQ RRS feed

  • Pregunta

  • soy muy nueva en LINQ, he ido aprendiendo por mi cuenta poco a poco, pero ahorita tengo un query de SQL que debo transcribir a LINQ y no tengo ni idea de por dónde empezar:

    SELECT Boletas.Material, 
    Materiales.Descripcion,  
    sum( CASE WHEN Boletas.Base = 4 THEN Boletas.Neto / 1000 ELSE 0 END) AS NetoB04,
    sum( CASE WHEN Boletas.Base = 4 THEN Boletas.Toneladas ELSE 0 END) AS ToneladasB04,
    sum( CASE WHEN Boletas.Base = 4 THEN Boletas.Importe ELSE 0 END) AS ImporteB04,
    sum( CASE WHEN Boletas.Base = 5 THEN Boletas.Neto / 1000 ELSE 0 END) AS NetoB05,
    sum( CASE WHEN Boletas.Base = 5 THEN Boletas.Toneladas ELSE 0 END) AS ToneladasB05,
    sum( CASE WHEN Boletas.Base = 5 THEN Boletas.Importe ELSE 0 END) AS ImporteB05, 
    Sum(Neto/1000) as NetoTotal, 
    Sum(Toneladas) as ToneladasTotal, 
    Sum(Importe) as ImporteTotal 
    FROM Boletas LEFT JOIN Materiales ON Boletas.Material = Materiales.Material 
    Where Boletas.NoEmpresa = 1 
    And  boletas.fechaFacturacion >= '20170101' and boletas.fechaFacturacion <= '20170114'  
    and boletas.status = 'A' 
    GROUP BY Boletas.Material, Materiales.Descripcion 
    HAVING (Boletas.Material = 1 or Boletas.Material = 3 or Boletas.Material = 4);
        Alguien podría apoyarme??


    cyndyrdz

    miércoles, 19 de abril de 2017 19:01

Respuestas

  • ...soy muy nueva en LINQ, he ido aprendiendo por mi cuenta poco a poco, pero ahorita tengo un query de SQL que debo transcribir a LINQ y no tengo ni idea de por dónde empezar

    Sobre la consulta sql que muestras las expresiones de la cláusula Having puedes subirlas a la cláusula Where, por lo demás creo que no hay mayores problemas. Por otro lado, el valor de la columna [Descripcion] puedes tomarlo a través de una sub-consulta correlacionada o a través de una combinatoria de filas, creo que no necesitas LEFT OUTER JOIN. Revisa el código t-sql que genera.

    var data = from b in boletas
    		   where b.NoEmpresa == 1
    			&& b.fechaFacturacion >= new DateTime(2017, 1, 1)
    			&& b.fechaFacturacion <= new DateTime(2017, 1, 14)
    			&& b.status == "A"
    			&& (b.Material == 1 || b.Material == 2 || b.Material == 3)
    		   group b by b.Material into g
    		   select new
    		   {
    			   Material = g.Key,
    			   Descripcion = materiales.FirstOrDefault(x => x.Material == g.Key).Material,
    			   NetoB04 = g.Where(x => x.Base == 4).Sum(x => x.Neto) / 1000,
    			   ToneladasB04 = g.Where(x => x.Base == 4).Sum(x => x.Toneladas) / 1000,
    			   ImporteB04 = g.Where(x => x.Base == 4).Sum(x => x.Importe) / 1000,
    			   NetoTotal = g.Sum(x => x.Neto) / 1000
    		   };



    Espero que la información proporcionada te haya sido de utilidad, quedo atento a tus comentarios.
    jueves, 20 de abril de 2017 4:18

Todas las respuestas

  • No es buena idea intentar pasar esa consulta a LINQ. Incluso aunque se consiguiese reescribir en LINQ cada una de las partes recurriendo a trucos diversos, al final la sentencia resultante será inmensamente compleja y muy poco eficiente en tiempo de ejecución.

    Donde dices "debo transcribir a LINQ " habría que plantear el POR QUÉ debes transcribirla a LINQ, y evaluar los motivos buscando alternativas para lograr el mismo objetivo.

    Si se trata de extraer los datos desde un servidor SQL y lo estás haciendo con un producto cliente que normalmente usa LINQ, tal como Entity Framework, hay alternativas. Por ejemplo, se puede encapsular la consulta en una Vista en el servidor y luego acceder a la Vista desde una consulta LINQ completamente trivial y directa. O si tienes EF6, usar el .Database del dbcontext para enviar la sentencia SQL en directo tal cual está, y dejar que EF se encargue de encapsular el resultado en entidades.

    Si, por el contrario, la razón por la que querías transcribirlo a LINQ es porque quieres consulta una estructura en memoria usando LINQ-to-Objects como si fuera una base de datos, entonces lo mejor es desglosar el objetivo en partes separadas y hacer las consultas individuales, y luego juntarlo todo mediante programación. Quedará más sencillo y comprensible que el equivalente hecho con LINQ.

    miércoles, 19 de abril de 2017 21:48
  • muchas gracias Alberto Población el requerimiento es que estoy en un nuevo proyecto de WPF con Entity Model, transcribiendo las consultas que estaban antes en un proyecto en Visual Basic,  el resultado del query SQL (recordset) sería el ItemsSource de un DataGrid de una de estas consultas. Sigo revisando...

    cyndyrdz

    miércoles, 19 de abril de 2017 22:04
  • ...soy muy nueva en LINQ, he ido aprendiendo por mi cuenta poco a poco, pero ahorita tengo un query de SQL que debo transcribir a LINQ y no tengo ni idea de por dónde empezar

    Sobre la consulta sql que muestras las expresiones de la cláusula Having puedes subirlas a la cláusula Where, por lo demás creo que no hay mayores problemas. Por otro lado, el valor de la columna [Descripcion] puedes tomarlo a través de una sub-consulta correlacionada o a través de una combinatoria de filas, creo que no necesitas LEFT OUTER JOIN. Revisa el código t-sql que genera.

    var data = from b in boletas
    		   where b.NoEmpresa == 1
    			&& b.fechaFacturacion >= new DateTime(2017, 1, 1)
    			&& b.fechaFacturacion <= new DateTime(2017, 1, 14)
    			&& b.status == "A"
    			&& (b.Material == 1 || b.Material == 2 || b.Material == 3)
    		   group b by b.Material into g
    		   select new
    		   {
    			   Material = g.Key,
    			   Descripcion = materiales.FirstOrDefault(x => x.Material == g.Key).Material,
    			   NetoB04 = g.Where(x => x.Base == 4).Sum(x => x.Neto) / 1000,
    			   ToneladasB04 = g.Where(x => x.Base == 4).Sum(x => x.Toneladas) / 1000,
    			   ImporteB04 = g.Where(x => x.Base == 4).Sum(x => x.Importe) / 1000,
    			   NetoTotal = g.Sum(x => x.Neto) / 1000
    		   };



    Espero que la información proporcionada te haya sido de utilidad, quedo atento a tus comentarios.
    jueves, 20 de abril de 2017 4:18
  • Muchas gracias Willams, voy a probarlo...

    cyndyrdz

    jueves, 20 de abril de 2017 14:03
  • Pues ya la implementé pero nunca la resuelve y marca un Time Out... :(

    cyndyrdz

    jueves, 20 de abril de 2017 16:16
  • ...Pues ya la implementé pero nunca la resuelve y marca un Time Out... 

    ¿Has intentado capturar las instrucciones sql que genera la consulta Linq?, si no lo has hecho podrías utilizar SQL Server Profiler, revisa el plan de ejecución que genera la consulta.

    Por otro lado, intenta quitar la sub-consulta (Descripcion = materiales.FirstOrDefault()...), ¿mejora el tiempo de respuesta?, de ser así cambia la sub-consulta a combinatoria de filas.

    Finalmente, entiendo que el diseño de las tablas contempla la creación de índices, ¿verdad?, de ser así considera el diseño del mismo para la definición en las expresiones de la cláusula WHERE.


    Espero que la información proporcionada te haya sido de utilidad, quedo atento a tus comentarios.
    jueves, 20 de abril de 2017 16:50
  • Willams, funciona bien si es que no involucro: && (b.Material == 1 || b.Material == 2 || b.Material == 3)

    no hay ningún índice que sea por Material, voy a revisar esto, muchas gracias


    cyndyrdz

    jueves, 20 de abril de 2017 20:11