2016-01-08 12 views
7

Io non sono bravo a espressione LINQ, oggi io sono in esecuzione in un problema strano come il seguito di interno join,LINQ to SQL unirsi genera SQL che unisce il IS NULL

var orders = (from q in dao.CurrentDBContext.New_OrderForm 
       join d in dao.CurrentDBContext.New_OrderGoodsDetail on q.billNum equals d.billNum 
       select new 
      {  
       q.billNum, 
       q.orderSource, 
       q.sourceOddNum  
       d.PPT  
      } 

Mentre ho tracciato il LINQ affermazione, mi sono confuso di quella Entity Framework convertirà la dichiarazione LINQ to SQL lo statment sotto

SELECT 
[Extent1].[billNum] AS [billNum], 
[Extent1].[orderSource] AS [orderSource], 
[Extent1].[sourceOddNum] AS [sourceOddNum], 
[Extent2].[PPT] AS [PPT] 
FROM [dbo].[New_OrderForm] AS [Extent1] 
INNER JOIN [dbo].[New_OrderGoodsDetail] AS [Extent2] 
      ON ([Extent1].[billNum] = [Extent2].[billNum]) OR 
       (([Extent1].[billNum] IS NULL) AND ([Extent2].[billNum] IS NULL)) 

sapete perché il segmento di SQL di seguito ha allegato automaticamente?

OR (([Extent1]. [BillNum] IS NULL) E ([Extent2]. [BillNum] IS NULL) "

non mi aspetto che quanto sopra aggiungerà automaticamente, dal momento che ha fatto .? rallentare le prestazioni di SQL Eventuali suggerimenti

+0

Qual è il ** tipo ** di 'q.billNum' e' d.billNum'? –

+0

entrambi di tipo di dati sono uguali, tipo stringa – beyond8848

+0

La colonna non è valida? Forse gli uguali in 'q.billNum equivale a d.billNum' presuppone che si desidera anche che' NULL == NULL' corrisponda? – Sam

risposta

3

Ecco cosa si può fare nel caso in cui non è possibile modificare le colonne billNum essere non annullabile.

In primo luogo, impostare l'opzione di cui parla @Giorgi

class CurrentDBContext 
{ 
    public CurrentDBContext() 
    { 
     Configuration.UseDatabaseNullSemantics = true; 
     // ... 
    } 
} 

quindi modificare la query LINQ to non utilizzarejoin, ma semplice where come questo

var orders = (from q in dao.CurrentDBContext.New_OrderForm 
       from d in dao.CurrentDBContext.New_OrderGoodsDetail 
       where q.billNum == d.billNum 
       select ... 

Il risultato sarà l'esatto Query SQL come quella che hai mostrato (conJOIN!) Senza la parte OR.

+0

Giusto per sottolineare che la condizione di join deve essere spostata nel punto in cui, come dice Ivan, avevo UseDatabaseNullSemantics = true, ma ancora utilizzato il join sintassi e non ha ottenuto i risultati previsti –

2

sulla scia @ risposta di Giorgi, la bandiera UseDatabaseNullSemantics non funziona con la parola chiave equals -. solo il == operando Così, al fine di ovviare a questo e garantire il join sul billNum non è parte della clausola OR questo approccio dovrebbe funzionare (in combinazione con il flag UseDatabaseNullSemantics):

var orders = (from q in dao.CurrentDBContext.New_OrderForm 
      from d in dao.CurrentDBContext.New_OrderGoodsDetail 
      where q.billNum == d.billNum 
      select new 
     {  
      q.billNum, 
      q.orderSource, 
      q.sourceOddNum  
      d.PPT  
     } 

Questo genererà la JOIN senza il OR.

+0

la seconda riga, deve essere "da ..." anziché "join ...". come join si aspettano gli uguali –

+0

Grazie - buon punto dato che la risposta è stata ampiamente ignorata! Ho modificato di conseguenza. – strickt01

3

Sembra che Linq traduca q.billNum equals d.billNum sia tale che include anche una corrispondenza valida nel caso in cui sia q.billNum che d.billNum siano NULL (in SQL NULL non è mai uguale a NULL, quindi l'OR nella query) .

Rendere entrambi i campi non annullabili sarebbe la soluzione migliore, a condizione che entrambi i campi non possano mai essere NULL.

In caso contrario, è possibile anche provare ad aggiungere una clausola where nella dichiarazione Linq per specificare che sia q.billNum che d.billNum non possono essere NULL. Con un po 'di fortuna, Linq riconoscerà che i valori nullable non sono possibili.

Nota: se si sta lavorando con Oracle, è necessario verificare le stringhe vuote e NULL (la stringa vuota è equivalente a NULL). Le stringhe vuote dovrebbero essere valide come valore valido in SQL Server.

Poiché quanto sopra non ha aiutato, è possibile provare a scrivere la query da soli. Se non sto sbagliare sarebbe qualcosa lungo le seguenti linee (supponendo var è un List<Order> nel codice di esempio - i risultati della query devono corrispondere alla classe che si sta utilizzando):

StringBuilder query = new StringBuilder(); 
query.AppendLine("SELECT [Extent1].[billNum] AS [billNum],"); 
query.AppendLine("  [Extent1].[orderSource] AS [orderSource],"); 
query.AppendLine("  [Extent1].[sourceOddNum] AS [sourceOddNum],"); 
query.AppendLine("  [Extent2].[PPT] AS [PPT]"); 
query.AppendLine("FROM [dbo].[New_OrderForm] AS [Extent1]"); 
query.AppendLine("INNER JOIN [dbo].[New_OrderGoodsDetail] AS [Extent2] ON [Extent1].[billNum] = [Extent2].[billNum]"); 

List<Order> orders = DbContext.Database.SqlQuery<Order>(query.ToString()).ToList(); 

ho usato simili soluzioni alternative per aggirare i problemi di prestazioni in passato.

+0

L'aggiunta di una clausola 'where' non rimuoverà la condizione" extra "' OR' (che richiederebbe a EF di eseguire l'ottimizzazione "globale" dell'espressione query). Tuttavia, potrebbe essere che il piano di esecuzione di SQL Server trarrà vantaggio dal fatto che 'billNum' non sarà mai' NULL'. –

+0

HI Sam, grazie per la tua risposta dettagliata, in questo momento forse non è fattibile dato che il sistema è online, il Cliente non mi permette di rendere il campo non annullabile, avevo provato questo 'var orders = (da q in dao. CurrentDBContext.New_OrderForm.Where (p => p.billNum! = Null) join d in dao.CurrentDBContext.New_OrderGoodsDetail.Where (p => p.billNum! = Null) su q.billNum equivale a d.billNum ', – beyond8848

+0

e provato anche "o" da q in dao.CurrentDBContext.New_OrderForm da d in dao.CurrentDBContext.New_OrderGoodsDetail .Where (p => p.billNum == q.billNum && p.billNum! = null && q.billNum! = null) '' – beyond8848

2

Se si utilizza EF6 provare a impostare

context.Configuration.UseDatabaseNullSemantics = true; 

e non genererà controlli NULL per le colonne.

According to documentation

Ad esempio (operando1 == operando2) viene tradotto come: (operando1 = operando2) se UseDatabaseNullSemantics è vero, rispettivamente (((operando1 = operando2) AND (NOT (operand1 è nullo o operand2 IS NULL))) OR ((operando 1 È NULL) E (operando 2 È NULL))) se UseDatabaseNullSemantics è falso.

+0

I test effettivi indicano che questo non risolve il problema (viene generato lo stesso SQL). Anche l'SQL nella documentazione è diverso rispetto a ciò che è nella domanda.Non sono sicuro di cosa sia 'UseDatabaseNullSemantics'. –

+0

esattamente, ho avuto un test, sembra che l'interruttore non è utile. :( – beyond8848

+0

Questo ha funzionato perfettamente per me. Impostare il flag su true rimosso il SQL O creato sul confronto stringa nullable. Grazie @Giorgi. – strickt01