Sto lavorando a un pezzo di codice, scritto da un collega, che si interfaccia con un'applicazione CRM utilizzata dalla nostra azienda. Ci sono due query LINQ to Entities in questo pezzo di codice che vengono eseguite più volte nella nostra applicazione, e mi è stato chiesto di ottimizzarle perché una di esse è molto lenta.La query LINQ to Entities richiede molto tempo per la compilazione, SQL corre veloce
Queste sono le domande:
prima interrogazione, questo uno compila praticamente all'istante. Si ottiene informazioni relative dal database CRM, filtrando da un elenco di ID relazione data dall'applicazione:
from relation in context.ADRELATION
where ((relationIds.Contains(relation.FIDADRELATION)) && (relation.FLDELETED != -1))
join addressTable in context.ADDRESS on relation.FIDADDRESS equals addressTable.FIDADDRESS
into temporaryAddressTable
from address in temporaryAddressTable.DefaultIfEmpty()
join mailAddressTable in context.ADDRESS on relation.FIDMAILADDRESS equals
mailAddressTable.FIDADDRESS into temporaryMailAddressTable
from mailAddress in temporaryMailAddressTable.DefaultIfEmpty()
select new { Relation = relation, Address = address, MailAddress = mailAddress };
La seconda query, che dura circa 4-5 secondi per compilare, e prende informazioni sulle persone da il database (ancora una volta filtrata da un elenco di ID):
from role in context.ROLE
join relationTable in context.ADRELATION on role.FIDADRELATION equals relationTable.FIDADRELATION into temporaryRelationTable
from relation in temporaryRelationTable.DefaultIfEmpty()
join personTable in context.PERSON on role.FIDPERS equals personTable.FIDPERS into temporaryPersonTable
from person in temporaryPersonTable.DefaultIfEmpty()
join nationalityTable in context.TBNATION on person.FIDTBNATION equals nationalityTable.FIDTBNATION into temporaryNationalities
from nationality in temporaryNationalities.DefaultIfEmpty()
join titelTable in context.TBTITLE on person.FIDTBTITLE equals titelTable.FIDTBTITLE into temporaryTitles
from title in temporaryTitles.DefaultIfEmpty()
join suffixTable in context.TBSUFFIX on person.FIDTBSUFFIX equals suffixTable.FIDTBSUFFIX into temporarySuffixes
from suffix in temporarySuffixes.DefaultIfEmpty()
where ((rolIds.Contains(role.FIDROLE)) && (relation.FLDELETED != -1))
select new { Role = role, Person = person, relation = relation, Nationality = nationality, Title = title.FTXTBTITLE, Suffix = suffix.FTXTBSUFFIX };
ho installato SQL Profiler e preso lo SQL da entrambe le query, poi corse in SQL Server Management Studio. Entrambe le query sono state eseguite molto velocemente, anche con un numero di ID ampio (~ 1000). Quindi il problema sembra essere nella compilazione della query LINQ.
Ho provato a utilizzare una query compilata, ma poiché questi possono contenere solo parametri primitivi, ho dovuto rimuovere la parte con il filtro e applicarla dopo la chiamata a Invoke(), quindi non sono sicuro se quello aiuta molto. Inoltre, poiché questo codice viene eseguito in un'operazione di servizio WCF, non sono sicuro che la query compilata continui a esistere anche nelle chiamate successive.
Infine, ciò che ho provato è stato selezionare solo una singola colonna nella seconda query. Sebbene questo ovviamente non mi dia le informazioni di cui ho bisogno, ho pensato che sarebbe più veloce delle ~ 200 colonne che stiamo selezionando ora. In questo caso, ci sono voluti ancora 4-5 secondi.
Non sono affatto un guru LINQ, quindi riesco a malapena a seguire questo codice (ho la sensazione che non sia scritto in modo ottimale, ma non riesco a metterci il dito sopra). Qualcuno potrebbe darmi un suggerimento sul motivo per cui questo problema potrebbe verificarsi?
L'unica soluzione che mi rimane è selezionare manualmente tutte le informazioni invece di unire tutte queste tabelle. Allora finirei con circa 5-6 domande. Non male, immagino, ma dato che non ho a che fare con SQL orribilmente inefficiente (o almeno un livello accettabile di inefficienza), speravo di impedirlo.
Grazie in anticipo, spero di aver chiarito le cose. In caso contrario, non esitate a chiedere e fornirò ulteriori dettagli.
Edit: ho finito per l'aggiunta di associazioni sul mio quadro entità (il database di destinazione non ha avuto le chiavi esterne specificate) e riscrivere la query nel seguente modo:
context.ROLE.Where(role => rolIds.Contains(role.FIDROLE) && role.Relation.FLDELETED != -1)
.Select(role => new
{
ContactId = role.FIDROLE,
Person = role.Person,
Nationality = role.Person.Nationality.FTXTBNATION,
Title = role.Person.Title.FTXTBTITLE,
Suffix = role.Person.Suffix.FTXTBSUFFIX
});
sembra molto più leggibile ed è anche più veloce.
Grazie per i suggerimenti, manterrò sicuramente quello sul rendere più query compilate per diversi numeri di argomenti in mente!
Come ho detto sulla risposta di Gabriel GM, li ho messi in un campo statico. Cercherò di nuovo solo per essere sicuro al 110%, suppongo. Funzionerà comunque, dal momento che potrò solo mettere la parte sulla clausola where nella query compilata? –
Ho aggiunto più cose. – usr
Oh, ottima idea della cosa cardinalità! Hai ragione, la maggior parte delle richieste avrà meno di 10 id di ruolo. Sicuramente qualcosa da guardare. Grazie! –