Ho il seguente codice, che un comportamento anomalo:Come risolvere super slow interrogazione EF/LINQ esegue più istruzioni SQL
TPM_USER user = UserManager.GetUser(context, UserId);
var tasks = (from t in user.TPM_TASK
where t.STAGEID > 0 && t.STAGEID != 3 && t.TPM_PROJECTVERSION.STAGEID <= 10
orderby t.DUEDATE, t.PROJECTID
select t);
La prima linea, UserManager.GetUser
semplicemente non una semplice ricerca nel database per ottenere il corretto Record TPM_USER
. Tuttavia, la seconda riga causa tutti i tipi di caos SQL.
Prima di tutto, esegue qui due istruzioni SQL. Il primo afferra ogni singola riga in TPM_TASK
che è collegato a tale utente, che è talvolta decine di migliaia di righe:
SELECT
-- Columns
FROM TPMDBO.TPM_USERTASKS "Extent1"
INNER JOIN TPMDBO.TPM_TASK "Extent2" ON "Extent1".TASKID = "Extent2".TASKID
WHERE "Extent1".USERID = :EntityKeyValue1
Questa query richiede circa 18 secondi utenti con un sacco di compiti. Mi aspetto che la clausola WHERE contenga anche i filtri STAGEID, che rimuoverebbero la maggior parte delle righe.
Avanti, sembra per eseguire una nuova query per ogni TPM_PROJECTVERSION
coppia nella lista di cui sopra:
SELECT
-- Columns
FROM TPMDBO.TPM_PROJECTVERSION "Extent1"
WHERE ("Extent1".PROJECTID = :EntityKeyValue1) AND ("Extent1".VERSIONID = :EntityKeyValue2)
Anche se questa query è veloce, è eseguito centinaia di volte se l'utente ha compiti in un sacco di progetti.
La query vorrei generare sarebbe simile:
SELECT
-- Columns
FROM TPMDBO.TPM_USERTASKS "Extent1"
INNER JOIN TPMDBO.TPM_TASK "Extent2" ON "Extent1".TASKID = "Extent2".TASKID
INNER JOIN TPMDBO.TPM_PROJECTVERSION "Extent3" ON "Extent2".PROJECTID = "Extent3".PROJECTID AND "Extent2".VERSIONID = "Extent3".VERSIONID
WHERE "Extent1".USERID = 5 and "Extent2".STAGEID > 0 and "Extent2".STAGEID <> 3 and "Extent3".STAGEID <= 10
La query precedente verrà eseguito in circa 1 secondo. In genere, è possibile specificare che JOIN
utilizza il metodo Include
. Tuttavia, questo non sembra funzionare sulle proprietà. In altre parole, non posso fare:
from t in user.TPM_TASK.Include("TPM_PROJECTVERSION")
Esiste un modo per ottimizzare questa istruzione LINQ? Sto usando .NET4 e Oracle come DB di back-end.
Soluzione:
Questa soluzione si basa sui suggerimenti di Kirk al di sotto, e opera da context.TPM_USERTASK
non possono essere interrogati direttamente:
var tasks = (from t in context.TPM_TASK.Include("TPM_PROJECTVERSION")
where t.TPM_USER.Any(y => y.USERID == UserId) &&
t.STAGEID > 0 && t.STAGEID != 3 && t.TPM_PROJECTVERSION.STAGEID <= 10
orderby t.DUEDATE, t.PROJECTID
select t);
E fa risultato in una nidificato SELECT
piuttosto che l'esecuzione di query TPM_USERTASK
direttamente, ma sembra abbastanza efficiente nessuno-meno.
Sfortunatamente, questa idea non funzionerà. 'TPM_TASK' non ha la proprietà' USERID'. Gli utenti si riferiscono alle attività tramite la tabella 'TPM_USERTASK', che non è possibile eseguire query direttamente poiché viene utilizzata in una relazione molti-a-molti. Forse farei meglio a creare una vista nel database o qualcosa del genere? –
Si può certamente scrivere la query per andare contro 'TPM_USERTASK' pure. Non sono sicuro di come sia impostato esattamente lo schema, ma qualcosa sulla falsariga di 'where t.TPM_TASK.Any (y => y.USERID == tUSER)' o semplicemente usare 'join' per inserire molti a molti. Sicuramente non devi scherzare con le visualizzazioni per raggiungere questo obiettivo. –
Ok, scherzi con quell'idea .. Ti farò sapere in pochi minuti .. –