Почему этот EF-запрос производит такой сумасшедший сгенерированный SQL? - PullRequest
0 голосов
/ 31 мая 2018
var statusId = db.WorkOrder.Where(w => w.OrderType.Name == "ReadAudit" && w.WorkOrderMapping.MeterOldTag == meterTag && w.OrderStatusId != 80)
                .OrderByDescending(w => w.CreationDatetime)
                .Select(r => r.OrderStatusId)                    
                .FirstOrDefault();

Это производит этот сумасшедший sql:

SELECT TOP (1) 
[Project1].[OrderStatusId] AS [OrderStatusId]
FROM ( SELECT 
    [Filter1].[OrderStatusId] AS [OrderStatusId], 
    [Filter1].[CreationDatetime] AS [CreationDatetime]
    FROM ( 
        SELECT [Extent1].[OrderStatusId] AS [OrderStatusId], 
               [Extent1].[CreationDatetime] AS [CreationDatetime], 
               [Extent3].[MeterOldTag] AS [MeterOldTag]
        FROM   [dbo].[WorkOrder] AS [Extent1]
        INNER JOIN [dbo].[OrderType] AS [Extent2] ON [Extent1].[OrderTypeKey] = [Extent2].[OrderTypeKey]
        LEFT OUTER JOIN [dbo].[WorkOrderMapping] AS [Extent3] ON [Extent1].[WorkOrderKey] = [Extent3].[WorkOrderMappingKey]
        WHERE (80 <> [Extent1].[OrderStatusId]) 
        AND (N'ReadAudit' = [Extent2].[Name])
    )  AS [Filter1]
    WHERE ([Filter1].[MeterOldTag] = @p__linq__0) OR (([Filter1].[MeterOldTag] IS NULL) AND (@p__linq__0 IS NULL))
)  AS [Project1]
ORDER BY [Project1].[CreationDatetime] DESC

И мне сказали, что он довольно сильно бьет по базе данных:

Таблица 'WorkOrder'.Число сканирований 30, логическое чтение 84403

Таблица «WorkOrderMapping».Сканирование 9, логическое чтение 16516

Запрос EF не кажется таким сложным.Есть ли способ сделать сгенерированный SQL более эффективным?

Ответы [ 2 ]

0 голосов
/ 01 июня 2018

Единственное, что «без ума» от этого SQL-запроса - это предикат MeterOldTag.Это написано так, потому что EF по умолчанию пишет запросы для эмуляции семантики сравнения C # для запросов LINQ.Если вы хотите провести простое сравнение на равенство в базе данных, установите UseDatabaseNullSemantics для вашего DbContext.

0 голосов
/ 31 мая 2018

Так работает Entity Framework.

Если вам нужен больший контроль, вы можете написать запрос самостоятельно с помощью Entity Framework Raw Queries, см. здесь и ниже (не завершено)пример того, как может выглядеть ваш запрос.

DbRawSqlQuery<Int32> query = db.Database.SqlQuery<Int32>("SELECT OrderStatusId FROM ... ");
var statusId = query.FirstOrDefault();

Edit:

Также посмотрите на фактический план запроса, который выполняется для этого запроса вЧтобы определить, присутствуют ли соответствующие индексы в соответствующих таблицах.

Рассмотрите возможность перемещения констант RealAudit и 80 в переменные, как показано ниже:

var orderType = "RealAudit";
var orderStatusId = 80;

var statusId = db.WorkOrder.Where(w => w.OrderType.Name == orderType
        && w.WorkOrderMapping.MeterOldTag == meterTag 
        && w.OrderStatusId != orderStatusId
    )
    .OrderByDescending(w => w.CreationDatetime)
    .Select(r => r.OrderStatusId)                    
    .FirstOrDefault();

Делая такони будут отображаться как параметры SQL в запросе, что-то вроде:

@p__linq__1 <> [Extent1].[OrderStatusId]) 
AND (@p__linq__2 = [Extent2].[Name])

Это позволяет использовать один план запроса для всех вариантов этого запроса, тогда как теперь вы получаете план запроса для отдельного значенияаргумент MeterOldTag.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...