• 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语句,尽量不用关联属性。。。


    以上就是我的一个简单粗糙的优化,也许得出的结论并不正确,但相信启发还是有的。希望大牛们能拿出更全面的优化方案。

  • 相关阅读:
    日志服务器 03-部署日志服务器(网络设备部分)
    日志服务器 02-部署日志服务器(主机部分)
    日志服务器 01-Linux日志系统syslog
    nyoj--523--亡命逃窜(BFS水题)
    hdoj--3488--Tour(KM)
    hdoj--1533--Going Home(KM)
    hdoj--2255--奔小康赚大钱(KM算法模板)
    LightOJ--1152--Hiding Gold(二分图奇偶建图)(好题)
    hdoj--5093--Battle ships(二分图经典建图)
    LightOJ--1149--Factors and Multiples(二分图好题)
  • 原文地址:https://www.cnblogs.com/xxfss2/p/2550826.html
Copyright © 2020-2023  润新知