2014-10-01 3 views
2

Я пытаюсь реплицировать результат любого существующего запроса из старого классического ASP-проекта. Запрос довольно прямолинейный.Entity Framework - Distinct и Max генерируют чудовищный (и медленный) запрос

SELECT DISTINCT TOP 10 MAX(H.HistID) as MaxHID, C.CompanyID,C.CompanyName, P.ProsID, P.ProsName, P.Online 
FROM HISTORY H, PROSPECTUS P, COMPANY C 
WHERE H.ProsID = P.ProsID 
and P.CompanyID = C.CompanyID 
and H.UserID = 2712 
GROUP BY C.CompanyID, C.CompanyName, P.ProsID, P.ProsName, P.Online 
ORDER BY MaxHID DESC 

Я (по крайней мере, пытался) повторить это с помощью Entity Framework с помощью следующего запроса:

MyContext.HistoryItems.Where(h => h.UserId == userId) 
    .GroupBy(h => new { h.Prospectus.Family.Id, h.ProsId }) 
    .Select(h => new { Max = h.Max(i => i.Id), Item = h.FirstOrDefault() }) 
    .OrderByDescending(h => h.Max) 
    .Take(10) 
    .Select(h => h.Item); 

(GroupBy() предназначен для репликации DISTINCT поведения от исходного запроса.)

Этот запрос приводит к тем же данным, но для выполнения требуется около 10 секунд. Я посмотрел на запрос, который создал EF, и это монстр.

SELECT TOP (10) 
    [Project6].[HistId] AS [HistId], 
    [Project6].[UserID] AS [UserID], 
    [Project6].[ProsID] AS [ProsID], 
    [Project6].[HDate] AS [HDate], 
    [Project6].[ProsDocId] AS [ProsDocId] 
    FROM (SELECT 
     [Project5].[HistId] AS [HistId], 
     [Project5].[UserID] AS [UserID], 
     [Project5].[ProsID] AS [ProsID], 
     [Project5].[HDate] AS [HDate], 
     [Project5].[ProsDocId] AS [ProsDocId], 
     [Project5].[C1] AS [C1] 
     FROM (SELECT 
      [Project4].[HistId] AS [HistId], 
      [Project4].[UserID] AS [UserID], 
      [Project4].[ProsID1] AS [ProsID], 
      [Project4].[HDate] AS [HDate], 
      [Project4].[ProsDocId] AS [ProsDocId], 
      (SELECT 
       MAX([Extent5].[HistId]) AS [A1] 
       FROM [dbo].[History] AS [Extent5] 
       INNER JOIN [dbo].[Prospectus] AS [Extent6] ON [Extent5].[ProsID] = [Extent6].[ProsId] 
       WHERE ([Extent5].[UserID] = @p__linq__0) AND (([Project4].[CompanyId] = [Extent6].[CompanyId]) OR (1 = 0)) AND ([Project4].[ProsID] = [Extent5].[ProsID])) AS [C1] 
      FROM (SELECT 
       [Project2].[ProsID] AS [ProsID], 
       [Project2].[CompanyId] AS [CompanyId], 
       [Limit1].[HistId] AS [HistId], 
       [Limit1].[UserID] AS [UserID], 
       [Limit1].[ProsID] AS [ProsID1], 
       [Limit1].[HDate] AS [HDate], 
       [Limit1].[ProsDocId] AS [ProsDocId] 
       FROM (SELECT 
        @p__linq__0 AS [p__linq__0], 
        [Distinct1].[ProsID] AS [ProsID], 
        [Distinct1].[CompanyId] AS [CompanyId] 
        FROM (SELECT DISTINCT 
         [Extent1].[ProsID] AS [ProsID], 
         [Extent2].[CompanyId] AS [CompanyId] 
         FROM [dbo].[History] AS [Extent1] 
         INNER JOIN [dbo].[Prospectus] AS [Extent2] ON [Extent1].[ProsID] = [Extent2].[ProsId] 
         WHERE [Extent1].[UserID] = @p__linq__0 
        ) AS [Distinct1]) AS [Project2] 
       OUTER APPLY (SELECT TOP (1) 
        [Extent3].[HistId] AS [HistId], 
        [Extent3].[UserID] AS [UserID], 
        [Extent3].[ProsID] AS [ProsID], 
        [Extent3].[HDate] AS [HDate], 
        [Extent3].[ProsDocId] AS [ProsDocId] 
        FROM [dbo].[History] AS [Extent3] 
        INNER JOIN [dbo].[Prospectus] AS [Extent4] ON [Extent3].[ProsID] = [Extent4].[ProsId] 
        WHERE ([Extent3].[UserID] = @p__linq__0) AND (([Project2].[CompanyId] = [Extent4].[CompanyId]) OR (1 = 0)) AND ([Project2].[ProsID] = [Extent3].[ProsID])) AS [Limit1] 
      ) AS [Project4] 
     ) AS [Project5] 
    ) AS [Project6] 
    ORDER BY [Project6].[C1] DESC 

В моем запросе Linq, очевидно, что-то не так, что EF создаст такой беспорядок. Я создал запрос EF как-то неэффективно?

+0

Вашего SQL-запрос неправильно также, вы должны были использовать INNER JOIN и поставить условия в пункт ON вместо того, чтобы все в ИНЕКЕ. – jannagy02

+3

Я знаю, что исходный запрос несколько неэффективен, хотя он не является «неправильным» каким-либо образом. И это утверждение никоим образом не является конструктивным. Я пытаюсь воспроизвести старые функции; говоря, что старый код не прав, ничего не помогает мне. –

+0

Это у вас GroupBy, которая вызывает вашу проблему. Позвольте мне посмотреть, смогу ли я построить более четкую для вас. –

ответ

5

Всякий раз, когда группировка входит в игру, я предпочитаю синтаксис запроса выше беглом синтаксиса.

Когда вы делаете ...

(
    from h in MyContext.HistoryItems 
    where h.UserId == userId) 
    group h by new { 
         h.CompanyId, 
         Comapany = h.Company.Name, 
         Prospectus = h.Prospectus.Name, 
         h.Prospectus.Online, 
         h.ProsId 
        } into grp 
    select new { 
        Max = grp.Max(i => i.Id), 
        grp.Key.CompanyId, 
        grp.Key.Comapany, 
        grp.Key.Prospectus, 
        grp.Key.Online, 
        grp.Key.ProsId, 
       } into proj 
    orderby proj.Max descending 
    select proj 
).Distinct().Take(10) 

... запрос должен быть намного чище, потому что теперь вы выбираете только свойства, которые будут появляться в финальной проекции, а FirstOrDefault() не полезла в. Соединений даже нет, потому что я позволяю EF определять соединения, используя свойства навигации, такие как h.Company.

Обратите внимание, что я попытался воспроизвести SQL-запрос. Запрос LINQ для меня не похож.

+0

Это, безусловно, самый чистый, который я вижу. Я понятия не имел, что использование прогнозов привело к такой драматической разнице. –

2

Попробуйте это, я не уверен, что это 100% рабочий

MyContext.HistoryItems 
     .Join(MyContext.ProspectusItems, a => a.ProsId, b => b.ProsId, (a,b) => new {a, b}) 
     .Join(MyContext.CompanyItems, a => a.b.CompanyId, b => b.CompanyId, (a,b) => new {a, b}) 
     .Where(a => a.a.a.UserId == 2712) 
     .GroupBy(x => new {x.b.CompanyID, x.bCompanyName, x.a.b.ProsID, x.a.b.ProsName, x.a.b.Online) 
     .Select(x => new {maxHistID = x.Max(x => x.a.a.HistID), x.Key.x.b.CompanyID, x.Key.x.bCompanyName, x.Key.x.a.b.ProsID, x.Key.x.a.b.ProsName, x.Key.x.a.b.Online). 
     .OrderByDesc(x => x.MaxHID) 
     .Take(10) 
+0

Это дает гораздо более чистый и эффективный запрос. Просто прискорбно, что требуемый код должен выглядеть таким грязным. Спасибо! –

0

Попробуйте это, что является более сопоставимой реализацией: MyContext.HistoryItems.Where(h => h.UserId == userId) GroupBy(h => new {h.Prospectus.Family.Id, h.ProsId},h=>h.HistId,(key,vals)=>new {key.Id, key.ProsId,Max=vals.Max()}) .OrderByDescending(h => h.Max) .Take(10)