Entity framewrok (linq to entity)查詢優化的一點摸索


對於linq to entity 生成的復雜sql語句相信已經困擾大家很久了,本人也是。從接觸實體框架到現在,一直都是邊學邊用,用啥學啥,沒有系統的學習過。同時所接觸項目也對性能方面沒什么要求,所以本人雖然對於EF生成的又臭又長的SQL相當不爽,但也沒花時間去優化過。

今天難得有空,便嘗試着做了小小的優化,略有收獲,分享出來,希望能對大家也有幫助。環境是EF4.0+SQL2008.


先說說我的思路

首先我們知道,從我們寫的LINQ或LAMBDA語句到生成的SQL 是有一個翻譯的過程的,這個翻譯是程序進行的,它必定按照已定的某種規則去翻譯。同樣的一個結果的查詢語句,我們選擇不同的寫法就會出現不同的SQL語句。所以同一個查詢語句嘗試不同的寫法,對比生成的SQL語句,也許能讓我們摸出EF的翻譯規則。


優化過程

我在項目中找了一段並不十分復雜也不算十分簡單的查詢語句,它涉及了5張表,4次join 。先來看看最初版本的代碼和生成的SQL吧

 1            var baseResult = from p in Database.CreditAuditDS
 2                              join p2 in Database.RoleDS on p.ToCreditFlow.RoleId equals p2.ROLESID
 3                              join p3 in Database.VEnterpriseDS on p.ToCreditDeclare.EnterpriseCode equals p3.Code
 4                              where p2.ROLESID == roleId && p.ToCreditFlow.Level == level && p.ToCreditDeclare.AuditState == p.ToCreditFlow.Id
 5                              select new ViewCreditAudit
 6                                  {
 7                                      Id = p.Id,
 8                                      DeclareId = p.ToCreditDeclare.Id,
 9                                      DeclareCode = p.ToCreditDeclare.DeclareCode,
10                                      Content = p.ToCreditDeclare.Content,
11                                      CreatedAt = p.CreatedAt,
12                                      EnterpriseName = p3.Name,
13                                      Result = p.Result,
14                                      Title = p.ToCreditDeclare.Title
15                                  };
 1 exec sp_executesql N'SELECT 
 2 [Extent1].[Id] AS [Id], 
 3 [Extent1].[DeclareId] AS [DeclareId], 
 4 [Extent10].[DeclareCode] AS [DeclareCode], 
 5 [Extent10].[Content] AS [Content], 
 6 [Extent1].[CreatedAt] AS [CreatedAt], 
 7 [Extent5].[Name] AS [Name], 
 8 [Extent1].[Result] AS [Result], 
 9 [Extent10].[Title] AS [Title]
10 FROM      [dbo].[T_Credit_Audit] AS [Extent1]
11 INNER JOIN [dbo].[M_ROLES] AS [Extent2] ON  EXISTS (SELECT 
12     1 AS [C1]
13     FROM    ( SELECT 1 AS X ) AS [SingleRowTable1]
14     LEFT OUTER JOIN  (SELECT 
15         [Extent3].[Id] AS [Id], 
16         [Extent3].[RoleId] AS [RoleId]
17         FROM [dbo].[T_Credit_Flow] AS [Extent3]
18         WHERE [Extent1].[FlowId] = [Extent3].[Id] ) AS [Project1] ON 1 = 1
19     LEFT OUTER JOIN  (SELECT 
20         [Extent4].[Id] AS [Id]
21         FROM [dbo].[T_Credit_Flow] AS [Extent4]
22         WHERE [Extent1].[FlowId] = [Extent4].[Id] ) AS [Project2] ON 1 = 1
23     WHERE [Project1].[RoleId] = [Extent2].[ROLESID]
24 )
25 INNER JOIN (SELECT 
26       [V_Enterprise].[Id] AS [Id], 
27       [V_Enterprise].[Code] AS [Code], 
28       [V_Enterprise].[AreaCode] AS [AreaCode], 
29       [V_Enterprise].[Name] AS [Name], 
30       [V_Enterprise].[Type] AS [Type]
31       FROM [dbo].[V_Enterprise] AS [V_Enterprise]) AS [Extent5] ON  EXISTS (SELECT 
32     1 AS [C1]
33     FROM    ( SELECT 1 AS X ) AS [SingleRowTable2]
34     LEFT OUTER JOIN  (SELECT 
35         [Extent6].[Id] AS [Id], 
36         [Extent6].[EnterpriseCode] AS [EnterpriseCode]
37         FROM [dbo].[T_Credit_Declare] AS [Extent6]
38         WHERE [Extent1].[DeclareId] = [Extent6].[Id] ) AS [Project4] ON 1 = 1
39     LEFT OUTER JOIN  (SELECT 
40         [Extent7].[Id] AS [Id], 
41         [Extent7].[EnterpriseCode] AS [EnterpriseCode]
42         FROM [dbo].[T_Credit_Declare] AS [Extent7]
43         WHERE [Extent1].[DeclareId] = [Extent7].[Id] ) AS [Project5] ON 1 = 1
44     WHERE ([Project4].[EnterpriseCode] = [Extent5].[Code]) OR (([Project5].[EnterpriseCode] IS NULL) AND ([Extent5].[Code] IS NULL))
45 )
46 LEFT OUTER JOIN [dbo].[T_Credit_Flow] AS [Extent8] ON [Extent1].[FlowId] = [Extent8].[Id]
47 INNER JOIN [dbo].[T_Credit_Declare] AS [Extent9] ON ([Extent1].[FlowId] = [Extent9].[AuditState]) AND ([Extent1].[DeclareId] = [Extent9].[Id])
48 LEFT OUTER JOIN [dbo].[T_Credit_Declare] AS [Extent10] ON [Extent1].[DeclareId] = [Extent10].[Id]
49 WHERE ([Extent2].[ROLESID] = @p__linq__0) AND ([Extent8].[Level] = @p__linq__1)',N'@p__linq__0 int,@p__linq__1 int',@p__linq__0=58,@p__linq__1=2

很長吧,這樣的SQL語句我已經在SQL SERVER PROFILER中見過無數次了。剛才說過,這個查詢只是對5個表已經連接查詢,理想狀態,也就是我們自己手寫代碼,4個inner join 就可以了。但是我們看看EF為我們生成的代碼吧,復雜的對於我這個SQL菜鳥都看不大懂了,反正不管是inner join 還是left outer join ,JOIN這個關鍵詞出現了一共9次。

 

接下來看看這個查詢語句的第二個版本和生成的SQL吧

 1             var baseResult = from p in Database.CreditDeclareDS
 2                              from p2 in p.ToCreditAudit
 3                              join p3 in Database.RoleDS on p2.ToCreditFlow.RoleId equals p3.ROLESID
 4                              join p4 in Database.VEnterpriseDS on p.EnterpriseCode equals p4.Code
 5                              where p3.ROLESID == roleId && p2.ToCreditFlow.Level == level && p.AuditState == p2.ToCreditFlow.Id
 6                              select new ViewCreditAudit
 7                                  {
 8                                      Id = p2.Id,
 9                                      DeclareId = p.Id,
10                                      DeclareCode = p.DeclareCode,
11                                      Content = p.Content,
12                                      CreatedAt = p2.CreatedAt,
13                                      EnterpriseName = p4.Name,
14                                      Result = p2.Result,
15                                      Title = p.Title
16                                  };
 1 exec sp_executesql N'SELECT 
 2 [Extent1].[Id] AS [Id], 
 3 [Extent2].[Id] AS [Id1], 
 4 [Extent1].[DeclareCode] AS [DeclareCode], 
 5 [Extent1].[Content] AS [Content], 
 6 [Extent2].[CreatedAt] AS [CreatedAt], 
 7 [Extent6].[Name] AS [Name], 
 8 [Extent2].[Result] AS [Result], 
 9 [Extent1].[Title] AS [Title]
10 FROM     [dbo].[T_Credit_Declare] AS [Extent1]
11 INNER JOIN [dbo].[T_Credit_Audit] AS [Extent2] ON ([Extent1].[Id] = [Extent2].[DeclareId]) AND ([Extent1].[AuditState] = [Extent2].[FlowId])
12 INNER JOIN [dbo].[M_ROLES] AS [Extent3] ON  EXISTS (SELECT 
13     1 AS [C1]
14     FROM    ( SELECT 1 AS X ) AS [SingleRowTable1]
15     LEFT OUTER JOIN  (SELECT 
16         [Extent4].[Id] AS [Id], 
17         [Extent4].[RoleId] AS [RoleId]
18         FROM [dbo].[T_Credit_Flow] AS [Extent4]
19         WHERE [Extent2].[FlowId] = [Extent4].[Id] ) AS [Project1] ON 1 = 1
20     LEFT OUTER JOIN  (SELECT 
21         [Extent5].[Id] AS [Id]
22         FROM [dbo].[T_Credit_Flow] AS [Extent5]
23         WHERE [Extent2].[FlowId] = [Extent5].[Id] ) AS [Project2] ON 1 = 1
24     WHERE [Project1].[RoleId] = [Extent3].[ROLESID]
25 )
26 INNER JOIN (SELECT 
27       [V_Enterprise].[Id] AS [Id], 
28       [V_Enterprise].[Code] AS [Code], 
29       [V_Enterprise].[AreaCode] AS [AreaCode], 
30       [V_Enterprise].[Name] AS [Name], 
31       [V_Enterprise].[Type] AS [Type]
32       FROM [dbo].[V_Enterprise] AS [V_Enterprise]) AS [Extent6] ON [Extent1].[EnterpriseCode] = [Extent6].[Code]
33 INNER JOIN [dbo].[T_Credit_Flow] AS [Extent7] ON [Extent2].[FlowId] = [Extent7].[Id]
34 WHERE ([Extent3].[ROLESID] = @p__linq__0) AND ([Extent7].[Level] = @p__linq__1)',N'@p__linq__0 int,@p__linq__1 int',@p__linq__0=58,@p__linq__1=2


當我看到這次生成的SQL時,我心中不由一喜:貌似看到曙光了。。。

首先生成的SQL語句縮短了很多字符,JOIN這個關鍵字這次只出現了6次。經過反復的對比,我發現了一些門道,於是按着這個門道,我再次修改。以下是最后的代碼和生成的SQL。

 1             var baseResult = from p in Database.CreditDeclareDS
 2                              from p2 in p.ToCreditAudit
 3                              join p5 in Database .CreditFlowDS on p2.FlowId equals p5 .Id 
 4                              join p3 in Database.RoleDS on p5.RoleId equals p3.ROLESID
 5                              join p4 in Database.VEnterpriseDS on p.EnterpriseCode equals p4.Code
 6                              where p3.ROLESID == roleId && p5.Level == level && p.AuditState == p5.Id
 7                              select new ViewCreditAudit
 8                              {
 9                                  Id = p2.Id,
10                                  DeclareId = p.Id,
11                                  DeclareCode = p.DeclareCode,
12                                  Content = p.Content,
13                                  CreatedAt = p2.CreatedAt,
14                                  EnterpriseName = p4.Name,
15                                  Result = p2.Result,
16                                  Title = p.Title
17                              };
 1 exec sp_executesql N'SELECT 
 2 [Extent1].[Id] AS [Id], 
 3 [Extent2].[Id] AS [Id1], 
 4 [Extent1].[DeclareCode] AS [DeclareCode], 
 5 [Extent1].[Content] AS [Content], 
 6 [Extent2].[CreatedAt] AS [CreatedAt], 
 7 [Extent5].[Name] AS [Name], 
 8 [Extent2].[Result] AS [Result], 
 9 [Extent1].[Title] AS [Title]
10 FROM     [dbo].[T_Credit_Declare] AS [Extent1]
11 INNER JOIN [dbo].[T_Credit_Audit] AS [Extent2] ON [Extent1].[Id] = [Extent2].[DeclareId]
12 INNER JOIN [dbo].[T_Credit_Flow] AS [Extent3] ON ([Extent2].[FlowId] = [Extent3].[Id]) AND ([Extent1].[AuditState] = [Extent3].[Id])
13 INNER JOIN [dbo].[M_ROLES] AS [Extent4] ON [Extent3].[RoleId] = [Extent4].[ROLESID]
14 INNER JOIN (SELECT 
15       [V_Enterprise].[Id] AS [Id], 
16       [V_Enterprise].[Code] AS [Code], 
17       [V_Enterprise].[AreaCode] AS [AreaCode], 
18       [V_Enterprise].[Name] AS [Name], 
19       [V_Enterprise].[Type] AS [Type]
20       FROM [dbo].[V_Enterprise] AS [V_Enterprise]) AS [Extent5] ON [Extent1].[EnterpriseCode] = [Extent5].[Code]
21 WHERE ([Extent4].[ROLESID] = @p__linq__0) AND ([Extent3].[Level] = @p__linq__1)',N'@p__linq__0 int,@p__linq__1 int',@p__linq__0=58,@p__linq__1=2

這才是我想要生成的SQL,4次inner join ,除了視圖V_Enterprise這里有點瑕疵,基本符合要求了。


說說我的分析

與其說是分析,不如說是觀察結果。

在我的第一個代碼版本中,我只用了兩次join,但是卻在from 和where 中用了兩個關聯屬性。

在我的第二個代碼版本中,我用了兩次join 和額外的from ,相當於三次join 用了一個關聯屬性。

在我的第三個代碼版本中,我相當於用了四次join,但沒有再使用關聯屬性。

那么,我們可以簡單的得出這樣的一個結果:盡量使用join語句,盡量不用關聯屬性。。。

 


以上就是我的一個簡單粗糙的優化,也許得出的結論並不正確,但相信啟發還是有的。希望大牛們能拿出更全面的優化方案。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM