2012-06-08 17 views
12

Tengo una consulta de linq a sql que devuelve algunas órdenes con saldo distinto a cero (de hecho, la consulta es un poco complicada, pero por simplicidad omití algunos detalles) Esta consulta también debe devolver pedidos sin CardItems (ambas sub-consultas devuelven NULL en T-SQL, y la comparación de dos NULLS da FALSE, por lo que convierto los valores de resultado NULL de las sub-consultas a 0 para comparar).Make Linq to Sql genera T-SQL con ISNULL en lugar de COALESCE

var q = (from o in db.Orders 
     where db.Cards(p => 
      p.OrderId == o.Id 
      && p.Sum + (db.CardItems.Where(i => i.IncomeId == p.Id) 
         .Sum(i => (double?)i.Amount) ?? 0) 
        != (db.CardItems.Where(i => i.DeductId == p.Id) 
         .Sum(i => (double?)i.Amount) ?? 0) 
      ).Any() 
     select o); 

El problema es, que la conversión de expresión suma (i => (doble?) I.Amount) ?? 0 produce el operador COALESCE, que es diez veces más lento que exactamente la misma consulta T-SQL con COALESCE reemplazado a ISNULL debido a una subconsulta en él. ¿Hay alguna posibilidad de generar ISNULL en esta situación?

+0

Como una cuestión técnica que esto tiene un montón de mérito. Sin embargo, como programador tengo que preguntar: este tipo de micro-optimización parece equivocado, ¿es importante? –

+4

@PreetSangha, ¿qué hay de malo en escribir un código de la manera correcta si sabes cuál es el problema? Considero que es un enfoque equivocado, cuando sabes sobre el problema, no haces nada al respecto ... Si obtienes voluntariamente un código que es 10 veces más lento de lo que podría ser, es bastante interesante llamar a la optimización ". mal "... – walther

+0

Creo que ?? La parte 0' resulta del coalesce – V4Vendetta

Respuesta

2

En mi experiencia, persuadir a LINQ en la generación del SQL lo que quieres es una molestia en el mejor de los casos. Si tiene una implementación de consulta que es mejor que linq (funciona correctamente y tiene un buen rendimiento), siga adelante y úsela, aunque solo sea para esta consulta.

La manera más rápida es probablemente con DataContext.ExecuteQuery<TResult>. Incluso hidratará y manejará los objetos devueltos por ti, al igual que si linq generara la consulta en sí.

http://msdn.microsoft.com/en-us/library/bb361109.aspx

Muchas personas prefieren poner este SQL en un procedimiento almacenado, especialmente en el código de producción. Entonces solo tiene que mapear el procedimiento almacenado en sus objetos linq, y funciona de la misma manera. Como suele ser el caso, ScottGu tiene un post bastante detallada sobre cómo hacerlo:

http://weblogs.asp.net/scottgu/archive/2007/08/16/linq-to-sql-part-6-retrieving-data-using-stored-procedures.aspx

0

Dado que está asumiendo que una fila sin una cantidad debe sumarse a cero, entonces puede filtrar las filas sin la cantidad y no preocuparse por IsNull o fusionarse.

&& p.Sum + (db.CardItems.Where(i => i.IncomeId == p.Id) 
        .Where(i=> i.Amount > 0) 
        .Sum(i => (double?)i.Amount) ?? 0) 
       != (db.CardItems.Where(i => i.DeductId == p.Id) 
        .Where(i=> i.Amount > 0) 
        .Sum(i => (double?)i.Amount) ?? 0) 

Dado que no sé sus objetos, que incluso puede ser capaz de eliminar los moldes (doble?) Y por defecto (??) los operadores

+0

Si remove null-coalescing ?? operador, su consulta no devolverá pedidos sin ningún artículo de tarjeta (el operador de SUMA devolverá NULO, la comparación con NULL siempre devuelve FALSO), será igual con '&& p.Sum + (db.CardItems.Where (i => i.IncomeId == p.Id) .Sum (i => i.Amount))! = (db.CardItems.Where (i => i.DeductId == p.Id) .Sum (i => i.Amount) '. Si se deja en su lugar, no le dará ninguna ventaja, ya que genera casi el mismo T-SQL que el origen. El problema no está en la cantidad NULL, esta columna no puede contener nulos, sino en NULL SUM. – Harm