2010-09-21 131 views
9

在我的项目中,我使用EntityFramework 4来处理数据。我发现一个简单的查询可怕的性能问题。当我查看由EF4生成的SQL查询中的分析器时,我感到很震惊。实体框架中的Linq查询4.糟糕的性能

我有我的实体数据模型的一些表:

Data Model

看起来很简单。我试图从所有相关导航属性中选择指定类别中的所有产品项目。

我写这个LINQ查询:

ObjectSet<ProductItem> objectSet = ...; 
int categoryId = ...; 

var res = from pi in objectSet.Include("Product").Include("Inventory").Include("Inventory.Storage") 
where pi.Product.CategoryId == categoryId 
select pi; 

EF生成此SQL查询:

SELECT [Project1].[pintId1]   AS [pintId], 
[Project1].[pintId]   AS [pintId1], 
[Project1].[intProductId]  AS [intProductId], 
[Project1].[nvcSupplier]  AS [nvcSupplier], 
[Project1].[ nvcArticle]  AS [ nvcArticle], 
[Project1].[nvcBarcode]  AS [nvcBarcode], 
[Project1].[bIsActive]  AS [bIsActive], 
[Project1].[dtDeleted]  AS [dtDeleted], 
[Project1].[pintId2]   AS [pintId2], 
[Project1].[nvcName]   AS [nvcName], 
[Project1].[intCategoryId] AS [intCategoryId], 
[Project1].[ncProductType] AS [ncProductType], 
[Project1].[C1]    AS [C1], 
[Project1].[pintId3]   AS [pintId3], 
[Project1].[intProductItemId] AS [intProductItemId], 
[Project1].[intStorageId]  AS [intStorageId], 
[Project1].[dAmount]   AS [dAmount], 
[Project1].[mPrice]   AS [mPrice], 
[Project1].[dtModified]  AS [dtModified], 
[Project1].[pintId4]   AS [pintId4], 
[Project1].[nvcName1]   AS [nvcName1], 
[Project1].[bIsDefault]  AS [bIsDefault] 
FROM  (SELECT [Extent1].[pintId]   AS [pintId], 
[Extent1].[intProductId] AS [intProductId], 
[Extent1].[nvcSupplier] AS [nvcSupplier], 
[Extent1].[ nvcArticle] AS [ nvcArticle], 
[Extent1].[nvcBarcode]  AS [nvcBarcode], 
[Extent1].[bIsActive]  AS [bIsActive], 
[Extent1].[dtDeleted]  AS [dtDeleted], 
[Extent2].[pintId]   AS [pintId1], 
[Extent3].[pintId]   AS [pintId2], 
[Extent3].[nvcName]  AS [nvcName], 
[Extent3].[intCategoryId] AS [intCategoryId], 
[Extent3].[ncProductType] AS [ncProductType], 
[Join3].[pintId1]   AS [pintId3], 
[Join3].[intProductItemId] AS [intProductItemId], 
[Join3].[intStorageId]  AS [intStorageId], 
[Join3].[dAmount]   AS [dAmount], 
[Join3].[mPrice]   AS [mPrice], 
[Join3].[dtModified]  AS [dtModified], 
[Join3].[pintId2]   AS [pintId4], 
[Join3].[nvcName]   AS [nvcName1], 
[Join3].[bIsDefault]  AS [bIsDefault], 
CASE 
WHEN ([Join3].[pintId1] IS NULL) THEN CAST(NULL AS int) 
ELSE 1 
END AS [C1] 
FROM [ProductItem] AS [Extent1] 
INNER JOIN [Product] AS [Extent2] 
ON [Extent1].[intProductId] = [Extent2].[pintId] 
LEFT OUTER JOIN [Product] AS [Extent3] 
ON [Extent1].[intProductId] = [Extent3].[pintId] 
LEFT OUTER JOIN (SELECT [Extent4].[pintId]   AS [pintId1], 
[Extent4].[intProductItemId] AS [intProductItemId], 
[Extent4].[intStorageId]  AS [intStorageId], 
[Extent4].[dAmount]   AS [dAmount], 
[Extent4].[mPrice]   AS [mPrice], 
[Extent4].[dtModified]  AS [dtModified], 
[Extent5].[pintId]   AS [pintId2], 
[Extent5].[nvcName]   AS [nvcName], 
[Extent5].[bIsDefault]  AS [bIsDefault] 
FROM [Inventory] AS [Extent4] 
INNER JOIN [Storage] AS [Extent5] 
ON [Extent4].[intStorageId] = [Extent5].[pintId]) AS [Join3] 
ON [Extent1].[pintId] = [Join3].[intProductItemId] 
WHERE [Extent2].[intCategoryId] = 8 /* @p__linq__0 */) AS [Project1] 
ORDER BY [Project1].[pintId1] ASC, 
[Project1].[pintId] ASC, 
[Project1].[pintId2] ASC, 
[Project1].[C1] ASC 

7000条记录在数据库中,并〜1000条记录中指定类别的这个查询的执行时间ID 10秒左右。

FROM [ProductItem] AS [Extent1] 
INNER JOIN [Product] AS [Extent2] 
ON [Extent1].[intProductId] = [Extent2].[pintId] 
LEFT OUTER JOIN [Product] AS [Extent3] 
ON [Extent1].[intProductId] = [Extent3].[pintId] 
***LEFT OUTER JOIN (SELECT ....*** 

嵌套选择加入...太可怕了......我试图改变LINQ查询,但我得到相同的SQL查询输出:如果看看这个,这并不奇怪。

使用存储过程的解决方案对我来说是不可接受的,因为我正在使用SQL Compact数据库。

+3

您的英语不是那么糟:)也是格式良好的问题。 +1 – Aren 2010-09-21 19:41:20

+1

您可以使用http://imgur.com/分享图像。 – Steven 2010-09-21 19:41:45

+2

包含什么?为什么不只是从pi的objectSet中pi.Product.CategoryId == categoryId select pi_? – 2010-09-21 19:42:03

回答

7

你在做Include("Product").Include("Inventory").Include("Inventory.Storage"),你想知道为什么这么多的记录被提取,为什么看到这么大的SQL查询?请确保您了解Include方法的含义。如果你想要一个简单的查询,请使用以下命令:但是

var res = 
    from pi in objectSet 
    where pi.Product.CategoryId == categoryId 
    select pi; 

请注意,这将可能被送到负载ProductsInventoriesStorages懒洋洋地,这可能会导致更多的查询时,遍历这些子集。

+0

+1好点 - 通过Product:ProductItem(1:*)和ProductItem:Inventory(1:*),一个产品将会加载一大堆额外的(可能不需要的)数据....难怪它很慢.... – 2010-09-21 20:25:26

0

我在考虑问题是在存储元素中的库存集合。您的查询将限制将Product,ProductItem和Inventory项目选定为指定的CategoryId的项目。但是,为了填充存储元素的清单集合,查询还必须返回使用相同StorageId的所有清单行(以及所有相应的ProductItem和产品行以获取这些额外的清单记录。)

I首先从存储元素中删除库存集合或删除相应的包含。