2012-05-08 2 views
3

Мой вопрос о Linq к SQL Performance, у меня есть строка SQL и преобразовать его в Linq к SQL:Linq к производительности SQL с группировкой

SQL запросов:

SELECT CONVERT(VARCHAR(10), ClockIn, 103) AS ClockDate, MIN(ClockIn) AS ClockIn, MAX(ClockOut) AS ClockOut, SUM(DATEDIFF(MINUTE, ClockIn, ClockOut)) AS [TotalTime] 
FROM TimeLog 
WHERE (EmployeeId = 10) 
GROUP BY CONVERT(VARCHAR(10), ClockIn, 103) 
ORDER BY ClockIn DESC 

LINQ запроса:

From u In objDC.TimeLogs 
Where u.EmployeeId = 10 
Group By Key = New With {u.ClockIn.Year, u.ClockIn.Month, u.ClockIn.Day} Into G = Group 
Order By G.First.ClockIn Descending 
Select New With {.ClockDate = Key.Day & "/" & Key.Month & "/" & Key.Year, 
.ClockIn = G.Min(Function(p) p.ClockIn), 
.ClockOut = G.Max(Function(p) p.ClockOut), 
.TotalTime = G.Sum(Function(p) SqlMethods.DateDiffMinute(p.ClockIn, p.ClockOut))} 

Сгенерированный строка запроса из LINQ в SQL Profiler был:

SELECT [t4].[value] AS [ClockDate], [t4].[value2] AS [ClockIn2], [t4].[value22] AS [ClockOut], [t4].[value3] AS [TotalTime] 
FROM (
SELECT ((((CONVERT(NVarChar,[t3].[value32])) + '/') + (CONVERT(NVarChar,[t3].[value222]))) + '/') + (CONVERT(NVarChar,[t3].[value22])) AS [value], [t3].[value] AS [value2], [t3].[value2] AS [value22], [t3].[value3], [t3].[value22] AS [value222], [t3].[value222] AS [value2222], [t3].[value32] 
FROM (
SELECT MIN([t2].[ClockIn]) AS [value], MAX([t2].[ClockOut]) AS [value2], SUM([t2].[value]) AS [value3], [t2].[value2] AS [value22], [t2].[value22] AS [value222], [t2].[value3] AS [value32] 
FROM (
SELECT DATEDIFF(Minute, [t1].[ClockIn], [t1].[ClockOut]) AS [value], [t1].[EmployeeId], [t1].[value] AS [value2], [t1].[value2] AS [value22], [t1].[value3], [t1].[ClockIn], [t1].[ClockOut] 
FROM (
SELECT DATEPART(Year, [t0].[ClockIn]) AS [value], DATEPART(Month, [t0].[ClockIn]) AS [value2], DATEPART(Day, [t0].[ClockIn]) AS [value3], [t0].[ClockIn], [t0].[ClockOut], [t0].[EmployeeId] 
FROM [dbo].[TimeLog] AS [t0] 
) AS [t1] 
) AS [t2] 
WHERE [t2].[EmployeeId] = 10 
GROUP BY [t2].[value2], [t2].[value22], [t2].[value3] 
) AS [t3] 
) AS [t4] 
ORDER BY (
SELECT [t6].[ClockIn] 
FROM (
SELECT TOP (1) [t5].[ClockIn] 
FROM [dbo].[TimeLog] AS [t5] 
WHERE ((([t4].[value222] IS NULL) AND (DATEPART(Year, [t5].[ClockIn]) IS NULL)) OR (([t4].[value222] IS NOT NULL) AND (DATEPART(Year, [t5].[ClockIn]) IS NOT NULL) AND ((([t4].[value222] IS NULL) AND (DATEPART(Year, [t5].[ClockIn]) IS NULL)) OR (([t4].[value222] IS NOT NULL) AND (DATEPART(Year, [t5].[ClockIn]) IS NOT NULL) AND ([t4].[value222] = DATEPART(Year, [t5].[ClockIn])))))) AND ((([t4].[value2222] IS NULL) AND (DATEPART(Month, [t5].[ClockIn]) IS NULL)) OR (([t4].[value2222] IS NOT NULL) AND (DATEPART(Month, [t5].[ClockIn]) IS NOT NULL) AND ((([t4].[value2222] IS NULL) AND (DATEPART(Month, [t5].[ClockIn]) IS NULL)) OR (([t4].[value2222] IS NOT NULL) AND (DATEPART(Month, [t5].[ClockIn]) IS NOT NULL) AND ([t4].[value2222] = DATEPART(Month, [t5].[ClockIn])))))) AND ((([t4].[value32] IS NULL) AND (DATEPART(Day, [t5].[ClockIn]) IS NULL)) OR (([t4].[value32] IS NOT NULL) AND (DATEPART(Day, [t5].[ClockIn]) IS NOT NULL) AND ((([t4].[value32] IS NULL) AND (DATEPART(Day, [t5].[ClockIn]) IS NULL)) OR (([t4]. 
[value32] IS NOT NULL) AND (DATEPART(Day, [t5].[ClockIn]) IS NOT NULL) AND ([t4].[value32] = DATEPART(Day, [t5].[ClockIn])))))) AND ([t5].[EmployeeId] = 10) 
) AS [t6] 
) DESC 

LINQ to SQL был слишком медленным, а план выполнения сгенерированного запроса по сравнению с SQL Query составлял 7% для человеческого письменного SQL-запроса и 97% для генерируемого Linq запроса.

Что случилось с моим запросом Linq to SQL? или это производительность и ограничение Linq?

+0

Добро пожаловать в мир негерметичных абстракций. – Robaticus

+0

Скомпилированный запрос доступен, скомпилированный запрос может. –

+3

С такой большой разницей в производительности вы рассмотрели использование хранимой процедуры для запроса? Практическое руководство. Использование хранимых процедур для возврата строк (LINQ to SQL): http://msdn.microsoft.com/en-us/library/bb386975.aspx –

ответ

4

Я думаю, что проблема в том, что вы получаете доступ к строк каждой группы в вашем OrderBy G.First заявлении и запуская поведение N + 1 в Linq к SQL, вы можете попробовать что-то вроде:

var query = objDC.TimeLogs 
      .Where(c => c.EmployeeId == 10) 
      .GroupBy(c => c.ClockIn.Date) 
      .OrderBy(g => g.Key) 
      .Select(g => new 
      { 
       Date = g.Key, 
       ClockIn = g.Min(c => c.ClockIn), 
       ClockOut = g.Max(c => c.ClockOut), 
      }) 
      .Select(g => new 
      { 
       g.Date, 
       g.ClockIn, 
       g.ClockOut, 
       TotalTime = g.ClockOut - g.ClockIn 
      }); 
+0

Большое спасибо Гийом, он решил проблему, я согласен с тобой, что проблема связана с G.First, я поменял свой запрос Linq в соответствии с вашим ответом.Я отправлю новый запрос linq, у меня получился тот же результат, но запрос был намного быстрее, и профилировщик дал мне 55% для письменного запроса и 45% для вновь созданного запроса, он был даже быстрее из исходного строкового запроса. Большое спасибо за вашу помощь. – Sameh

+0

без проблем, рад, что смогу помочь – Guillaume86

0

Опять же, это запрос linq, основанный на рекомендации Гийома.

Большое спасибо Гийом, он решил проблему, я согласен с вами, проблема связана с G.First.

Я изменил мой запрос Linq в соответствии с вашим ответом:

From u In objDC.TimeLogs 
Where u.EmployeeId = 10 
Group By key = New With {u.ClockIn.Date} Into G = Group 
Order By key.Date Descending 
Select New With { 
    .ClockDate = key.Date, 
    .ClockIn = G.Min(Function(p) p.ClockIn), 
    .ClockOut = G.Max(Function(p) p.ClockOut), 
    .TotalTime = G.Sum(Function(p) SqlMethods.DateDiffMinute(p.ClockIn, p.ClockOut))/60} 

я получил тот же результат, но запрос был намного быстрее, и профайлер дал мне 55% по письменному запросу и 45% для вновь сгенерированный запрос, он был даже быстрее из исходного строкового запроса.

Большое спасибо за помощь.

Смежные вопросы