参数化查询会创建不利的执行计划。针对非NULL的参数进行优化

时间:2018-10-31 14:29:03

标签: sql-server sql-execution-plan

作为一个警告,我正在使用从实体框架生成的SQL查询,但是实体框架与该问题无关。

某些情况: 我正在尝试从一批4,000个C#对象中提取特定记录,并对其执行更新或插入操作。我没有记录的主键,因为对象来自API,所以我必须使用一组唯一的列来提取正确的记录。

(简化的)查询及其执行计划:

参数化查询(出于演示目的,将参数声明为1)

SELECT [x].[Id]
      FROM [Gradebook].[AssignmentScore] AS [x]
      WHERE
         ( ( ((([x].[CourseSectionAssignment_Id] = @__CSA_1) AND ([x].[Student_Id] = @__S_ID_1)) AND ([x].[AssessmentGBID] = @__A_GBID_1))
          OR ((([x].[CourseSectionAssignment_Id] = @__CSA_2) AND ([x].[Student_Id] = @__S_ID_2)) AND ([x].[AssessmentGBID] = @__A_GBID_2)) )
          OR ((([x].[CourseSectionAssignment_Id] = @__CSA_3) AND ([x].[Student_Id] = @__S_ID_3)) AND ([x].[AssessmentGBID] = @__A_GBID_3)) )

这是(不利的)执行计划:

Parameterized query execution plan

文字值查询:

SELECT [x].[Id]
      FROM [Gradebook].[AssignmentScore] AS [x]
      WHERE
       ( ( ((([x].[CourseSectionAssignment_Id] = 1) AND ([x].[Student_Id] = 2)) AND ([x].[AssessmentGBID] = 3))
        OR ((([x].[CourseSectionAssignment_Id] = 4) AND ([x].[Student_Id] = 5)) AND ([x].[AssessmentGBID] = 6)) )
        OR ((([x].[CourseSectionAssignment_Id] = 7) AND ([x].[Student_Id] = 8)) AND ([x].[AssessmentGBID] = 9)) )

这是(有利的)执行计划:

Execution plan of query with literal values

我知道为什么,或者至少我相信为什么,执行计划是不同的。那是因为优化器不知道参数是否为NULL,必须围绕这种情况进行优化。使用NULL测试文字查询会创建不利的执行计划。 (为什么参数嗅探不会看到值不为null并创建更好的执行计划?)

当前,在C#代码中,我正在手动使用表达式树用表达式常量替换对象的属性,以便生成的查询是文字查询。据我所知,文字会强制SQL Server每次生成一个新的执行计划,这并不好。

我希望参数化查询生成有利的执行计划。到目前为止,我发现的唯一答案是使用OPTION(RECOMPILE)提示,这并不是我想要的,因为它会强制重新创建执行计划。

每次使用参数化查询时,如何使用相同的有利执行计划?

1 个答案:

答案 0 :(得分:1)

根据我的经验,如果无法解决不良索引的问题,这将始终产生良好的执行计划。

SELECT [x].[Id]
FROM [Gradebook].[AssignmentScore] AS [x]
WHERE [x].[CourseSectionAssignment_Id] = 1
AND [x].[Student_Id] = 2
AND [x].[AssessmentGBID] = 3
UNION
SELECT [x].[Id]
FROM [Gradebook].[AssignmentScore] AS [x]
WHERE [x].[CourseSectionAssignment_Id] = 4
AND [x].[Student_Id] = 5
AND [x].[AssessmentGBID] = 6
UNION
SELECT [x].[Id]
FROM [Gradebook].[AssignmentScore] AS [x]
WHERE [x].[CourseSectionAssignment_Id] = 7
AND [x].[Student_Id] = 8
AND [x].[AssessmentGBID] = 9

或者,您可以使用“有效”组合创建一个临时表,然后在这三个字段上简单地将其联接。