2014-02-12 52 views
1

我试图优化以下简单实体框架查询retreieve所有的产品从同一组实体框架简单的子查询生成难看SQL

var query = from p in ctx.Products 
      where p.GroupId == (from q in ctx.Products 
           where q.Id == new Guid(".....")          
           select q.GroupId).FirstOrDefault() 
      select p; 

检查使用SQL Server Profiler我看到这个生成的SQL后查询

SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[GroupId] AS [GroupId], 
[Extent1].[Name] AS [Name], 
[Extent1].[Code] AS [Code] 
FROM  [inv].[Products] AS [Extent1] 
LEFT OUTER JOIN (SELECT TOP (1) [Extent2].[GroupId] AS [GroupId] 
    FROM [inv].[Products] AS [Extent2] 
    WHERE cast('.....' as uniqueidentifier) = [Extent2].[Id]) AS [Limit1] ON 1 = 1 
LEFT OUTER JOIN (SELECT TOP (1) [Extent3].[GroupId] AS [GroupId] 
    FROM [inv].[Products] AS [Extent3] 
    WHERE cast('.....' as uniqueidentifier) = [Extent3].[Id]) AS [Limit2] ON 1 = 1 
LEFT OUTER JOIN (SELECT TOP (1) [Extent4].[GroupId] AS [GroupId] 
    FROM [inv].[Products] AS [Extent4] 
    WHERE cast('.....' as uniqueidentifier) = [Extent4].[Id]) AS [Limit3] ON 1 = 1 
LEFT OUTER JOIN (SELECT TOP (1) [Extent5].[GroupId] AS [GroupId] 
    FROM [inv].[Products] AS [Extent5] 
    WHERE cast('.....' as uniqueidentifier) = [Extent5].[Id]) AS [Limit4] ON 1 = 1 
LEFT OUTER JOIN (SELECT TOP (1) [Extent6].[Id] AS [Id] 
    FROM [inv].[Products] AS [Extent6] 
    WHERE cast('.....' as uniqueidentifier) = [Extent6].[Id]) AS [Limit5] ON 1 = 1 
LEFT OUTER JOIN (SELECT TOP (1) [Extent7].[Id] AS [Id] 
    FROM [inv].[Products] AS [Extent7] 
    WHERE cast('.....' as uniqueidentifier) = [Extent7].[Id]) AS [Limit6] ON 1 = 1 
WHERE ([Extent1].[GroupId] = (CASE WHEN ([Limit1].[GroupId] IS NULL) 
            THEN cast('00000000-0000-0000-0000-000000000000' as uniqueidentifier) 
            ELSE [Limit2].[GroupId] END)) 
AND (CASE WHEN ([Limit3].[GroupId] IS NULL) 
      THEN cast('00000000-0000-0000-0000-000000000000' as uniqueidentifier) 
      ELSE [Limit4].[GroupId] END IS NOT NULL) 

为什么它会生成这么多相同的JOIN? 有没有任何选项可以删除最终的空铸件? 你能否建议一些方法来改善生成的sql?

我还测试了LINQPad和一个正在生成普通的SQL

-- Region Parameters 
DECLARE @p0 UniqueIdentifier = '....' 
-- EndRegion 
SELECT [t0].[Id], [t0].[GroupId], [t0].[Name], [t0].[Description], [t0].[Code] 
FROM [inv].[Products] AS [t0] 
WHERE [t0].[GroupId] = ((
    SELECT TOP (1) [t1].[GroupId] 
    FROM [inv].[Products] AS [t1] 
    WHERE [t1].[Id] = @p0 
    )) 
+0

如果你从你的应用程序获得不同的结果在LINQPad,请检查您使用的每个EF的版本并检查您的代码以确保在运行时没有任何内容被添加到表达式树中。 –

+0

我正在使用EF 6.0,没有其他任何东西被添加到表达式树 –

回答

1

如果你真的在这里处理主键(id),我不确定你需要FirstOrDefault。将下面的完成相同的目标:

var query = from groupProduct in ctx.Products 
      where groupProduct.Id == someGuid // This should return only one result 
      join childProduct in ctx.Products 
      on groupProduct.Id equals childProduct.GroupId 
      select childProduct 

和生成的SQL是

SELECT 
[Extent2].[Id] AS [Id], 
[Extent2].[GroupId] AS [GroupId], 
[Extent2].[Name] AS [Name], 
[Extent2].[Code] AS [Code] 
FROM [inv].[Products] AS [Extent1] 
INNER JOIN [inv].[Products] AS [Extent2] ON [Extent1].[Id] = [Extent2].[GroupId] 
WHERE cast('....' as uniqueidentifier) = [Extent1].[Id] 
+0

的确是这样的,尽管你的代码有一个小错误 –

+0

它应该是它应该在groupProduct.RecId等于childProduct.RecId而不是它应该在groupProduct.RecId等于childProduct.Id –

+0

@liviumamelluc看起来这是一个自引用连接。我想我已翻转并修复它。它不应该在parent.pk = child.pk,而是parent.pk = child.fk(GroupId)。 –

0

试试下面的两个声明,但应该产生平滑的SQL,如果这是你所追求的:

var groupId = ctx.Products.Where(p=> p.Id = new Guid(...).Select(p=> p.GroupId).FirstOrDefault(); 

if(groupId.HasValue) 
{ 
    var query = ctx.Products.Where(p=> p.GroupId == groupId).Select(p=> p); 
} 

这假定Id是一个int,如果不是,则使用适当的边界检查。

+0

是的,它是更平滑的SQL,但它正在做2往返SQL,我试图优化它 –

+0

你没有很多控制EF如何生成SQL,我会运行一些性能计数器并查看哪些性能更​​好。 – Maess

+0

可能是EF优化此groupBy var query = ctx.Products.GroupBy(p => p.GroupId).Where(g => g.Any(q.Id == new Guid(“.....”)) ) – bobah75