优化实体框架生成的 SQL Server 执行计划
Posted
技术标签:
【中文标题】优化实体框架生成的 SQL Server 执行计划【英文标题】:Optimize Entity Framework Generated SQL Server Execution Plan 【发布时间】:2012-06-01 19:07:07 【问题描述】:我有一个数据结构,它基本上是一个带有标签字典的文档。我正在尝试恢复给定表单类型的所有文档,这些文档具有名为“姓氏”的标签和标签值为“史密斯”的标签。可能有 0..N 个与文档关联的“姓氏”标签。
我正在使用以下 linq 查询尝试将源文档与具有匹配标签的子文档进行匹配:
DB.Documents
.Where(doc => doc.FormID == pd.IndexForm.FormID)
.Where(doc => doc.Document_StringIndex_ReadOnly
.Join(Fields,
dsi => new FieldName = dsi.FieldName, FieldValue = dsi.StringValue ,
dsi2 => new FieldName = dsi2.FieldName, FieldValue = dsi2.StringValue ,
(dsi, dsi2) => dsi.Document).Count() > 0);
使用 .ToTraceString() 输出时会生成以下查询
SELECT
[Project1].*
FROM ( SELECT
[Extent1].*
(SELECT
COUNT(cast(1 as bit)) AS [A1]
FROM [dbo].[Document_StringIndex_ReadOnly] AS [Extent2]
INNER JOIN (SELECT [Extent3].*
FROM [dbo].[Document] AS [Extent3]
INNER JOIN [dbo].[Document_StringIndex_ReadOnly] AS [Extent4] ON [Extent3].[DocumentID] = [Extent4].[DocumentID] ) AS [Join1] ON (([Extent2].[FieldName] = [Join1].[FieldName]) OR (([Extent2].[FieldName] IS NULL) AND ([Join1].[FieldName] IS NULL))) AND (([Extent2].[StringValue] = [Join1].[StringValue]) OR (([Extent2].[StringValue] IS NULL) AND ([Join1].[StringValue] IS NULL)))
LEFT OUTER JOIN [dbo].[Document] AS [Extent5] ON [Extent2].[DocumentID] = [Extent5].[DocumentID]
WHERE ([Extent1].[DocumentID] = [Extent2].[DocumentID]) AND ([Join1].[DocumentID1] = @p__linq__7) AND ([Join1].[FieldName] = @p__linq__8)) AS [C1]
FROM [dbo].[Document] AS [Extent1]
WHERE [Extent1].[FormID] = @p__linq__5
) AS [Project1]
WHERE [Project1].[C1] > 0
如果我为我的参数直接替换常量(如下所示),查询执行得非常快。但是,如果我保留参数,则查询需要几分钟时间。
SELECT
[Project1].*
FROM ( SELECT
[Extent1].*
(SELECT
COUNT(cast(1 as bit)) AS [A1]
FROM [dbo].[Document_StringIndex_ReadOnly] AS [Extent2]
INNER JOIN (SELECT [Extent3].*
FROM [dbo].[Document] AS [Extent3]
INNER JOIN [dbo].[Document_StringIndex_ReadOnly] AS [Extent4] ON [Extent3].[DocumentID] = [Extent4].[DocumentID] ) AS [Join1] ON (([Extent2].[FieldName] = [Join1].[FieldName]) OR (([Extent2].[FieldName] IS NULL) AND ([Join1].[FieldName] IS NULL))) AND (([Extent2].[StringValue] = [Join1].[StringValue]) OR (([Extent2].[StringValue] IS NULL) AND ([Join1].[StringValue] IS NULL)))
LEFT OUTER JOIN [dbo].[Document] AS [Extent5] ON [Extent2].[DocumentID] = [Extent5].[DocumentID]
WHERE ([Extent1].[DocumentID] = [Extent2].[DocumentID]) AND ([Join1].[DocumentID1] = 1015) AND ([Join1].[FieldName] = 'DDKey')) AS [C1]
FROM [dbo].[Document] AS [Extent1]
WHERE [Extent1].[FormID] = 22
) AS [Project1]
WHERE [Project1].[C1] > 0
生成执行计划后,我了解到如果直接代入参数值,SQL Server 会执行索引查找,我的查询速度很快。只要我保留参数,SQL Server 就会执行索引扫描,并且我的查询会超时。有什么方法可以让 SQL Server 一直在寻找?我可以强制实体框架不使用参数化查询吗?
【问题讨论】:
您使用的是什么版本的 Linq to Entities? 【参考方案1】:在生成的SQL中,这一行
[Join1].[FieldName] = @p__linq__8
可能是问题。
如果FieldName
是varchar(...)
并且@p__linq__8
是nvarchar(...)
,则此子句将导致表扫描,因为参数类型与索引类型不匹配。
当您直接替换“DDKey”时,类型匹配,因此您获得索引查找。尝试使用 N'DDkey' 进行查询,看看是否获得了表扫描。
这是各种版本的 Linq to Sql 和 Linq to Entities 的问题,但可能会在以后的版本中修复。
如果您无法更新到最新版本,解决此问题的一种方法是将FieldName
更改为nvarchar(...)
。
【讨论】:
谢谢。将我的参数更改为 varchar 会生成正确的执行计划。使用 nvarchar 列类型重建表和索引需要一段时间,但看起来这是我的问题。以上是关于优化实体框架生成的 SQL Server 执行计划的主要内容,如果未能解决你的问题,请参考以下文章
SQL Server中参数化SQL写法遇到parameter sniff ,导致不合理执行计划重用的一种解决方案
在 SQL Server 执行计划中优化 Table Spool