2014-02-12 50 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