2016-11-23 34 views
3

我正在做一些測試與EFLinq to entities來嘗試和提高我的應用程序性能。Linq到實體順序由sql

我只是注意到一些奇怪的東西(對我來說),我無法解釋,也無法確定是否會產生相當大的開銷。

這裏是我的LINQ:

var result = from n in query 
     orderby n.PersonId 
     select new 
     { 
      id = n.Id, 
      appointmentId = n.AppointmentId, 
      message = n.Message, 
      wasRead = n.Read, 
      canDismiss = (n.Appointment.Status != AppointmentStatus.Waiting), 
      date = n.IssueDateUtc 
     }; 

這是生成的sql:

SELECT 
    [Project1].[Id] AS [Id], 
    [Project1].[AppointmentId] AS [AppointmentId], 
    [Project1].[Message] AS [Message], 
    [Project1].[Read] AS [Read], 
    [Project1].[C1] AS [C1], 
    [Project1].[IssueDateUtc] AS [IssueDateUtc] 
    FROM (SELECT 
     [Extent1].[Id] AS [Id], 
     [Extent1].[Read] AS [Read], 
     [Extent1].[Message] AS [Message], 
     [Extent1].[IssueDateUtc] AS [IssueDateUtc], 
     [Extent1].[AppointmentId] AS [AppointmentId], 
     [Extent1].[PersonId] AS [PersonId], 
     CASE WHEN (NOT ((1 = [Extent2].[Status]) AND ([Extent2].[Status] IS NOT NULL))) THEN cast(1 as bit) WHEN (1 = [Extent2].[Status]) THEN cast(0 as bit) END AS [C1] 
     FROM [dbo].[Notification] AS [Extent1] 
     LEFT OUTER JOIN [dbo].[Appointment] AS [Extent2] ON [Extent1].[AppointmentId] = [Extent2].[Id] 
     WHERE [Extent1].[PersonId] = @p__linq__0 
    ) AS [Project1] 
    **ORDER BY [Project1].[PersonId] ASC** 

我不明白,需要一種羣體的結果在其他投影(Project1),而這似乎工作得很好:

SELECT 
     [Extent1].[Id] AS [Id], 
     [Extent1].[Read] AS [Read], 
     [Extent1].[Message] AS [Message], 
     [Extent1].[IssueDateUtc] AS [IssueDateUtc], 
     [Extent1].[AppointmentId] AS [AppointmentId], 
     [Extent1].[PersonId] AS [PersonId], 
     CASE WHEN (NOT ((1 = [Extent2].[Status]) AND ([Extent2].[Status] IS NOT NULL))) THEN cast(1 as bit) WHEN (1 = [Extent2].[Status]) THEN cast(0 as bit) END AS [C1] 
     FROM [dbo].[Notification] AS [Extent1] 
     LEFT OUTER JOIN [dbo].[Appointment] AS [Extent2] ON [Extent1].[AppointmentId] = [Extent2].[Id] 
     WHERE [Extent1].[PersonId] = @p__linq__0 
     **ORDER BY [Extent1].[PersonId] ASC** 

我發現了相當數量的由ef和linq生成的有問題的sql,我開始懷疑我是不是更好,只是寫了原始的sqls。

問題是:生成的sql額外的代碼是需要擔心的一些問題?爲什麼這個投影是必要的?

編輯添加一個新的LINQ

正如在評論中提到的,也許是冗長被後續查詢正在運行造成的。我改寫了LINQ只使用一個查詢對象,結果還是一樣:

dbSet.Where(n => n.PersonId == id).Select(n => new 
      { 
       Id = n.Id, 
       AppointmentId = n.AppointmentId, 
       Message = n.Message, 
       Read = n.Read, 
       CanBeDismissed = (n.Appointment.Status != AppointmentStatus.Waiting), 
       IssueDate = n.IssueDateUtc 
      }).OrderBy(n => n.Id).ToList(); 

執行計劃(兩者相同sqls

actual execution plan

編輯2

剛從簡單計數中得到這個查詢。

dbSet.Count(x => x.Id == 1 && x.Read == false); 

SELECT 
[GroupBy1].[A1] AS [C1] 
FROM (SELECT 
    COUNT(1) AS [A1] 
    FROM [dbo].[Notification] AS [Extent1] 
    WHERE ([Extent1].[PersonId] = 19) AND (0 = [Extent1].[Read]) 
) AS [GroupBy1] 

預計:

SELECT 
    COUNT(1) AS [A1] 
    FROM [dbo].[Notification] AS [Extent1] 
    WHERE ([Extent1].[PersonId] = 19) AND (0 = [Extent1].[Read]) 

我不明白,所有這種包裝是從,爲什麼來了。

+0

我想'query'是另一個LINQ到SQL查詢,那就是你所看到的嵌套在投影內部。這正是你在用LINQ做什麼。如果你想擺脫它,只需將'OrderBy'和'Select'添加到'query'中。但是如果使用後續的LINQ查詢使得代碼更清晰,請不要擔心......如果有的話,投影的開銷很小。如果有疑問,請比較執行計劃。但是,如果你看到性能問題,肯定不會是由該投影造成的。 –

+0

關於您對LINQ-to-SQL生成查詢的擔心......我發現Linq-to-SQL生成優秀(但通常是冗長)的SQL查詢,甚至在複雜場景中更多。我會說這比大多數程序員能夠實現的要好。但是,它當然也需要寫得很好的LINQ查詢。所以你最好仔細檢查一下生成的SQL ......但總的來說,不用擔心,LINQ通常對於何時使用連接,交叉應用或其他表現最好的東西做出了很好的選擇,但我實際上經常從看在LINQ生成的SQL;) –

+0

我總是在學習新東西。我只是擔心,因爲我沒有對sgdb進行過多的研究,而且我的產品也在不斷增長,現在我花時間分析db訪問,看到相當可怕的代碼,並且只是重寫了表達式,我能夠在大約60% 。無論如何,我只用一個查詢重寫了linq,結果是一樣的。我會將其添加到問題中,請查看。我認爲額外的代碼是由於使用匿名類型而生成的。 – victor

回答

1

我在我的機器上放了一個小樣本項目。在第一個示例中引發投影的是對CanBeDismissed字段的條件計算,導致SQL中出現CASE WHEN。如果你不這樣做,實體框架將不會做額外的預測。

所以與條件檢查:

db.Notifications 
    .Where(n => n.AppointmentId == 1) 
    .OrderBy(n => n.Id) 
    .Select(n => new 
    { 
     Id = n.Id, 
     Message = n.Message, 
     HasMessage = n.Message != null 
    }).ToList(); 

產生的SQL是:

SELECT 
    [Project1].[Id] AS [Id], 
    [Project1].[Message] AS [Message], 
    [Project1].[C1] AS [C1] 
    FROM (SELECT 
     [Extent1].[Id] AS [Id], 
     [Extent1].[Message] AS [Message], 
     CASE WHEN ([Extent1].[Message] IS NOT NULL) THEN cast(1 as bit) ELSE cast(0 as bit) END AS [C1] 
     FROM [dbo].[Notifications] AS [Extent1] 
     WHERE 1 = [Extent1].[AppointmentId] 
    ) AS [Project1] 
    ORDER BY [Project1].[Id] ASC 

讓我補充所產生的執行計劃,供日後參考:

Execution plan with projection

如果你離開它:

db.Notifications 
    .Where(n => n.AppointmentId == 1) 
    .OrderBy(n => n.Id) 
    .Select(n => new 
    { 
     Id = n.Id, 
     Message = n.Message 
    }).ToList(); 

無投影由EF完成:

SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Message] AS [Message] 
    FROM [dbo].[Notifications] AS [Extent1] 
    WHERE 1 = [Extent1].[AppointmentId] 
    ORDER BY [Extent1].[Id] ASC 

所以這是爲什麼。 count示例同樣適用:如果發生任何分組,EF將添加一個額外的投影,這會使查詢更加冗長。但是重要的一點是,正如你對這個問題的評論所討論的那樣,它不會損害性能,所以沒有必要擔心這個額外的預測。

讓我證明這現在就先加入下面的查詢,在那裏我剛纔刪除第一個查詢的pojection和移動的排序依據的內層查詢的執行計劃:

SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[Message] AS [Message], 
CASE WHEN ([Extent1].[Message] IS NOT NULL) THEN cast(1 as bit) ELSE cast(0 as bit) END AS [C1] 
FROM [dbo].[Notifications] AS [Extent1] 
WHERE 1 = [Extent1].[AppointmentId] 
ORDER BY [Extent1].[Id] ASC 

Execution plan wihtout projection

完全一樣 - 沒有額外的任務,成本分配保持不變。 SQL查詢優化器將很好地優化這些項目。

所以再次,不要擔心預測 - 他們不會傷害你,雖然我同意他們看起來有時不必要的冗長。但這裏有兩件事情可以幫助你:

性能問題:

首先,如果您遇到性能問題與查詢,看看爲什麼在您發佈的執行計劃發生一個Clustered Index Scan。這並不總是一些索引問題的跡象,但它往往是。你的問題可能源於此。

擺脫unneccesary預測的:

如果你仍然想在所有擺脫這些預測的(或至少更多)的情況下,有實體框架的核心1.0 - 它實際上會產生比甚至更好的SQL EF 6.可能需要考慮遷移到EF 6,但請注意它不具備EF 6所具備的全部功能,因此,如果您使用的是EF Core 1.0不提供的功能,則該選項可能不是一種選擇。但它將與完整的.NET Framework 4.x一起工作!

這裏有一個例子,當我執行我的回答的第一個LINQ聲明什麼EF核心1.0生產:

SELECT [n].[Id], [n].[Message], CASE 
    WHEN [n].[Message] IS NULL 
    THEN CAST(0 AS BIT) ELSE CAST(1 AS BIT) 
END 
FROM [Notifications] AS [n] 
WHERE ([n].[Id] = 1) AND ([n].[Id] = 1) 
ORDER BY [n].[Id] 
+0

非常好,謝謝。我認爲發生索引掃描是因爲必須檢查每一行以確定它的fk列是否爲X。這個條件解決後,你可以看到我發佈的執行計劃,似乎數據庫做索引尋求獲取ID爲X的行來執行連接。我*認爲*這是正確的,但不知道。 儘管如此,EF在某些情況下認爲有必要添加額外的投影,這很奇怪。 – victor