2012-06-08 14 views
12

Ho una query da linq a sql che restituisce alcuni ordini con saldo diverso da zero (in effetti, la query è un po 'complicata, ma per semplicità ho omesso alcuni dettagli). Anche questa query dovrebbe restituire gli ordini senza CardItems (entrambe le sottoquery restituiscono NULL in T-SQL e il confronto tra due NULLS dà FALSE, quindi converto i valori NULL dei risultati delle sottoquery a 0 per il confronto).Invia Linq a Sql genera T-SQL con ISNULL invece di 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); 

Il problema è, che la conversione espressione Sum (i => (doppia?) I.Amount) ?? 0 produce l'operatore COALESCE, che è dieci volte più lento della stessa query T-SQL con COALESCE sostituito a ISNULL a causa della sottoquery in esso. C'è qualche possibilità di generare ISNULL in questa situazione?

+0

come una questione tecnica questo ha un sacco di meriti. Tuttavia, come programmatore devo chiedere: questo tipo di micro-ottimizzazione sembra sbagliato, è importante? –

+4

@PreetSangha, cosa c'è di "sbagliato" nello scrivere un codice nel modo giusto se sai dov'è il problema? Considero un approccio sbagliato, quando anche se conosci il problema, non fai nulla al riguardo ... Se produci volentieri un codice che è 10 volte più lento di quanto potrebbe essere, è piuttosto interessante chiamare l'ottimizzazione " sbagliato "... – walther

+0

Penso che ?? Parte 0 sta per la coalesce – V4Vendetta

risposta

2

Nella mia esperienza, lusinghe LINQ nel generare l'SQL vuoi è una seccatura al meglio. Se hai un'implementazione di query migliore di linq (funziona correttamente e funziona bene), vai avanti e usala, anche se solo per questa query.

Il modo più veloce è probabilmente con DataContext.ExecuteQuery<TResult>. Idrerà e gestirà anche gli oggetti restituiti, proprio come se linq generasse la query stessa.

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

Molte persone preferiscono mettere questo SQL in una stored procedure, in particolare nel codice di produzione. Quindi devi solo mappare la stored procedure nei tuoi oggetti linq e funziona allo stesso modo. Come spesso accade, ScottGu ha un messaggio piuttosto dettagliate su come farlo:

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

0

Poiché si presuppone che una riga senza un importo debba essere sommata come zero, è possibile filtrare solo le righe senza l'importo e non preoccuparsi di IsNull o coalizione.

&& 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) 

Dal momento che non conosco i vostri oggetti, si può anche essere in grado di rimuovere i calchi (doppio?) E di default (??) operatori

+0

Se rimuovi coalescenza nulla? operatore, la tua query non restituirà Ordini senza CardItems (l'operatore SUM restituirà NULL, il confronto con NULL restituisce sempre FALSE), sarà ugualmente 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) '. Se lasciare posto su di esso non darà alcun vantaggio, a causa della generazione quasi dello stesso T-SQL dell'origine.Il problema non è in NULL Amount, questa colonna non è nemmeno annullabile, ma in NULL SUM. – Harm