简体   繁体   English

实体框架简单的子查询生成难看的SQL

[英]entity framework simple subquery generates ugly sql

I'm trying to optimize the following simple Entity Framework query to retreieve all the products from the same group 我正在尝试优化以下简单的Entity Framework查询,以检索同一组中的所有产品

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;

After checking the generated sql using SQL Server Profiler I see this query 使用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)

Why is it generating so many identical JOINs? 为什么会生成这么多相同的JOIN? Is there any option to remove the final null casting? 是否可以删除最终的空类型转换? Could you please suggest ways to improve the generated sql? 您能否提出一些改进生成的sql的方法?

I've tested also with LINQPad and that one is generating normal 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
    ))

If you're really dealing with a primary key (id) here, I'm not sure that you need the FirstOrDefault. 如果您确实在这里处理主键(id),则不确定您是否需要FirstOrDefault。 Would the following accomplish the same goal: 以下内容是否可以实现相同的目标:

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

And the generated SQL is 生成的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]

Try the following, its two statements but should produce smoother SQL if that is what you are after: 尝试以下两个语句,但是如果您要这样做,则应产生更平滑的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);
}

This assumes that Id is an int, if it is not, use the appropriate boundary check. 假定Id是一个int,如果不是,则使用适当的边界检查。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM