Tengo el siguiente código, que está portando mal:Cómo fijar super lento consulta EF/LINQ ejecutar varias instrucciones 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 primera línea, UserManager.GetUser
simplemente realiza una búsqueda simple en la base de datos para obtener la correcta TPM_USER
registro. Sin embargo, la segunda línea causa todo tipo de caos de SQL.
En primer lugar, está ejecutando dos instrucciones SQL aquí. El primero de ellos agarra cada hilera en TPM_TASK
que está vinculado a ese usuario, lo cual es a veces decenas de miles de filas:
SELECT
-- Columns
FROM TPMDBO.TPM_USERTASKS "Extent1"
INNER JOIN TPMDBO.TPM_TASK "Extent2" ON "Extent1".TASKID = "Extent2".TASKID
WHERE "Extent1".USERID = :EntityKeyValue1
Esta consulta tarda unos 18 segundos en los usuarios con un montón de tareas. Esperaría que la cláusula WHERE también contenga los filtros STAGEID, lo que eliminaría la mayoría de las filas.
A continuación, parece para ejecutar una consulta nueva para cada TPM_PROJECTVERSION
par en la lista anterior:
SELECT
-- Columns
FROM TPMDBO.TPM_PROJECTVERSION "Extent1"
WHERE ("Extent1".PROJECTID = :EntityKeyValue1) AND ("Extent1".VERSIONID = :EntityKeyValue2)
A pesar de que esta consulta es rápido, es ejecutado varios cientos de veces si el usuario tiene tareas en un montón de proyectos.
La consulta me gustaría generar sería algo como:
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 consulta anterior se ejecutaría en aproximadamente 1 segundo. Normalmente, podría especificar que JOIN
usando el método Include
. Sin embargo, esto no parece funcionar en las propiedades. En otras palabras, no puedo hacer:
from t in user.TPM_TASK.Include("TPM_PROJECTVERSION")
¿Hay alguna manera de optimizar esta declaración de LINQ? Estoy usando .NET4 y Oracle como el back-end DB.
Solución:
Esta solución se basa en sugerencias de Kirk abajo, y trabaja desde context.TPM_USERTASK
no se puede consultar directamente:
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);
Se hace resultado en un anidado SELECT
en lugar de consultar TPM_USERTASK
directamente, pero parece bastante eficiente sin embargo.
Lamentablemente, esta idea no funcionará. 'TPM_TASK' no tiene la propiedad' USERID'. Los usuarios se relacionan con las tareas a través de la tabla 'TPM_USERTASK', que no puede consultar directamente ya que se usa en una relación muchos a muchos. ¿Tal vez sería mejor crear una vista en la base de datos o algo así? –
Sin duda, puede escribir la consulta para ir contra 'TPM_USERTASK' también. No estoy seguro de cómo se configura exactamente su esquema, pero algo en la línea de 'donde t.TPM_TASK.Any (y => y.USERID == t.USER)' o simplemente use 'join' para traer el muchos a muchos. Definitivamente no debes jugar con las vistas para lograr esto. –
De acuerdo, jugando con esa idea ... Te lo haré saber en unos minutos ... –