无法理解从此Entity Framework查询生成的SQL

时间:2010-11-18 21:55:01

标签: c# sql-server entity-framework

我创建了一个实体框架模型,其中包含Northwind数据库中的两个表来测试它的一些功能:产品和CAtegories。

它自动在Category和Product之间创建了一个关联,它是0..1到*。

我写了这个简单的查询:

var beverages = from p in db.Products.Include("Category")
                where p.Category.CategoryName == "Beverages"
                select p;

var beverageList = beverages.ToList();

我运行SQL事件探查器并运行代码,以便我可以看到它生成的SQL,这就是它生成的内容:

SELECT 
    [Extent1].[ProductID] AS [ProductID], 
    [Extent1].[ProductName] AS [ProductName], 
    [Extent1].[SupplierID] AS [SupplierID], 
    [Extent1].[QuantityPerUnit] AS [QuantityPerUnit], 
    [Extent1].[UnitPrice] AS [UnitPrice], 
    [Extent1].[UnitsInStock] AS [UnitsInStock], 
    [Extent1].[UnitsOnOrder] AS [UnitsOnOrder], 
    [Extent1].[ReorderLevel] AS [ReorderLevel], 
    [Extent1].[Discontinued] AS [Discontinued], 
    [Extent3].[CategoryID] AS [CategoryID], 
    [Extent3].[CategoryName] AS [CategoryName], 
    [Extent3].[Description] AS [Description], 
    [Extent3].[Picture] AS [Picture]
FROM   [dbo].[Products] AS [Extent1]
    INNER JOIN [dbo].[Categories] AS [Extent2] 
        ON [Extent1].[CategoryID] = [Extent2].CategoryID]
    LEFT OUTER JOIN [dbo].[Categories] AS [Extent3] 
        ON [Extent1].[CategoryID] = [Extent3].[CategoryID]
WHERE N'Beverages' = [Extent2].[CategoryName]

我很好奇为什么查询内部连接到类别然后离开连接到它。 select语句使用左连接表中的字段。有人能帮我理解这个的原因吗?如果我删除左连接并将选择列表更改为从Extent2中提取,则会得到与此查询相同的结果。在什么情况下这不是真的?

4 个答案:

答案 0 :(得分:2)

[Extent3]Include(Category)的实现,Include不应影响“主”表产品的选择结果,因此LEFT JOIN(产品中的所有记录和右侧的一些记录)表类别)。

[Extent2]实际上是按相关表类别过滤所有记录,名称为“饮料”,所以在这种情况下它是强限制(INNER JOIN

为什么两个? :)因为为每个语句解析表达式和自动生成(包括,在哪里)

答案 1 :(得分:1)

您会注意到该查询正在从类别表别名SELECT的副本中提取Extent3列表中的所有列,但它是检查CategoryName 对照复制别名Extent2

换句话说,在这种情况下,EF的查询生成没有意识到你是Include()并通过同一个表限制查询,所以它盲目地使用两个副本。

不幸的是,除了解释发生了什么之外,我对EF的经验还不够先进,无法提出解决方案......

答案 2 :(得分:1)

djacobson和igor很好地解释了为什么会这样。我个人使用实体框架的方式,我完全避免使用Include。根据您计划对数据执行的操作,您可以执行以下操作:

var beverages = from p in db.Products
                select new {p, p.Category} into pc
                where pc.Category.CategoryName == "Beverages"
                select pc;                    
return beverages.ToList().Select(pc => pc.p);

...至少在EF 4.0中,它只会产生一个内连接。实体框架足够聪明,可以使产品的Category属性填充从数据库中带回来的类别。

当然,SQL Server很可能会优化一些东西,所以这实际上不会给你带来任何好处。

答案 3 :(得分:0)

(如果查询相同,但评论字段对此限制太多,则不直接回答您的问题)

如果你忽略了.Include(),它是不是加载它(因为在哪里)?一般来说,使用投影代替Include()更有意义:

var beverages = from p in db.Products.Include("Category")
                where p.Category.CategoryName == "Beverages"
                select new { Product = p, Category = p.Category };

var beverageList = beverages.ToList();