6

In primo luogo ho provato ARITHABORT OFF su SSMS è ancora meno di 1 secondo.Ef Le interrogazioni Linq sono scadute, ma le stesse query meno di 1 secondo su SSMS

Io uso EntityFramework: 6.1.3 e SQL Azure S1 tier (cercherò con Tier 3 e ti faccio sapere se qualcosa cambia.)

Io uso EF Profiler per ottenere SQL generato da LINQ. Ho interrogato tutti i linq che ho condiviso, sono tutti meno di 1 secondo su SSMS.

Ho 3 milioni di registrazioni sulla tabella AuditLog. Un cliente con ID 3 ha 170 K registra che l'altro cliente con ID 35 ha 125 record. Minimizzerò il codice.

AuditLog Modello:

public class AuditLog 
    { 
    public long? CustomerId { get; set; } 

    [ForeignKey("CustomerId")] 
    public virtual CustomerSummary Customer { get; set; } 

    [Required] 
    [Index] 
    public DateTime CreatedDate { get; set; } 
    } 

prima query:

if (customer != null) 
    { 
     var customerId = customer.Id; 
     var result= Dbset.Where(x => x.CustomerId == customerId).OrderByDescending(x => x.CreatedDate).Skip(0).Take(25).ToList(); 
    } 

se provo con il cliente che ha 170k righe, si dà il tempo fuori eccezione. Se provo con il cliente che ha 125 record, va bene.

Seconda query: È lo stesso con il primo che includo solo i clienti.

if (customer != null) 
    { 
     var customerId = customer.Id; 
     var result= Dbset.Where(x => x.CustomerId == customerId).OrderByDescending(x => x.CreatedDate).Skip(0).Take(25).Include(x => x.Customer).ToList(); 
    } 

Il risultato è opposto alla prima query. se provo con il cliente che ha 170k righe, va bene. Se provo con un cliente che ha 125 record, emette un'eccezione di timeout.

Terza query: È lo stesso con la prima query, ma corrisponde a long? su dove per customerId.

if (customer != null) 
    { 
     long? customerId = customer.Id; 
     var result= Dbset.Where(x => x.CustomerId == customerId).OrderByDescending(x => x.CreatedDate).Skip(0).Take(25).ToList(); 
    } 

Il risultato è opposto alla prima query. se provo con il cliente che ha 170k righe, va bene. Se provo con un cliente che ha 125 record, emette un'eccezione di timeout.

Quarta domanda: È lo stesso con la seconda query, ma corrisponde a long? su dove per customerId.

if (customer != null) 
    { 
     long? customerId = customer.Id; 
     var result= Dbset.Where(x => x.CustomerId == customerId).OrderByDescending(x => x.CreatedDate).Skip(0).Take(25).Include(x => x.Customer).ToList(); 
    } 

Il risultato è l'opposto della seconda query. se provo con il cliente che ha 170k righe, dà un'eccezione time-out. Se provo con il cliente che ha 125 record, va bene.

Sono davvero confuso. Perché l'inner join o la modifica del parametro match a long? stanno cambiando i risultati? E perché tutte queste query vengono eseguite in meno di 1 secondo su SSMS e danno errori su ef linq?

Errore:

{System.Data.SqlClient.SqlException (0x80131904): Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. ---> System.ComponentModel.Win32Exception (0x80004005): The wait operation timed out at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)

Aggiornamento (19/04/2016):

Dopo Ivan Stoev suggerimento su commenti.

Have you tried (just for the sake of test) using hardcoded 3 and 35 instead of customerId variable?

Non ho ricevuto alcun errore e le query sono più veloci come su SSMS.

Aggiornamento (20/04/2016): Il vero problema è sniffing dei parametri. Quando ho incluso o modificato il parametro in nullable, in realtà ho creato altre query e altri piani di query. Ho creato alcuni piani con il cliente che ha 125 record e gli altri con il cliente che ha 170k record di queste 4 query. Ecco perché ho avuto risultati diversi.

+0

Come stai confrontando i risultati? Stai confrontando l'SQL generato da EF o stai scrivendo il tuo? –

+0

Sql generato da EF. –

+0

Questo è interessante. Potrebbe essere utile anche fornire la versione di EF che stai utilizzando. –

risposta

4

Quello che stai vivendo è un risultato del cosiddetto Parameter Sniffing Problem. Non conosco una soluzione generale semplice finora, quindi di solito suggerisco una soluzione eliminando alcuni dei parametri di query SQL vincolando manualmente i valori costanti all'interno delle espressioni, come in EntityFramework LINQ query count fails but query returns result. How to optimize LINQ query?.

per lo scenario, vorrei suggerire il seguente metodo estensione personalizzata:

public static class QueryableExtensions 
{ 
    public static IQueryable<T> WhereEquals<T, TValue>(this IQueryable<T> source, Expression<Func<T, TValue>> selector, TValue value) 
    { 
     var predicate = Expression.Lambda<Func<T, bool>>(
      Expression.Equal(selector.Body, Expression.Constant(value)), 
      selector.Parameters); 
     return source.Where(predicate); 
    } 
} 

e quindi aggiornare il frammento come questo

if (customer != null) 
{ 
    var result= Dbset.WhereEquals(x => x.CustomerId.Value, customer.Id) 
     .OrderByDescending(x => x.CreatedDate) 
     .Skip(0).Take(25) 
     .Include(x => x.Customer) 
     .ToList(); 
} 
+0

Ciao Ivan, 'var ListItems = (da pl in _context.Order dove! Pl.SentDateUTC.HasValue seleziona pl) .ToList();' è questo caso di Parameter Sniffing? Perché sopra Linq semplice ha fallito con l'eccezione Timeout mentre la query funziona. puoi aiutarmi a risolvere questo problema? –

+0

@yogendarji Ciao, il tuo caso è diverso, perché la query non usa parametri. Fondamentalmente stai cercando 'pl.SentDateUTC == null'. Molto probabilmente sta facendo una scansione completa della tabella. Ma non dovrebbe richiedere così tanto tempo, quanti record hai nella tabella db? –

+0

Ho circa 150k di record nella tabella. il piano di query mi suggerisce di creare l'indice Non cluster. Devo creare un indice su quella colonna? –