SQL Server 2005执行计划问题

时间:2011-03-01 09:06:32

标签: sql-server sql-execution-plan

目前,我在我们的应用程序中找到了一个查询,其执行计划是“索引扫描”。嗯,这里有一些背景知识:

  1. 它有三列,输入为“idType:bigint”
  2. 手中只有200件吹制品
  3. 有列(a:PK,b:FK,c:FK),我们有两个索引(一个聚簇索引(b,c),一个PK非聚簇索引(a))
  4. 以下是我们的查询:

    exec sp_executesql N'select b,a from table where b in (@P0, @P1, @P2,
         @P3, @P4, @P5, @P6, @P7, @P8, @P9)',
         N'@P0 bigint, @P1 bigint, @P2 bigint, @P3 bigint, @P4 bigint, @P5 bigint,
           @P6 bigint, @P7 bigint, @P8 bigint, @P9 bigint', 
         94, 161, 4, 50, 166, 52, 53, 90, 100, 123
    

    它从执行计划中显示为pk索引上的“索引扫描”......出了什么问题?

    如果我使用相同的查询但不使用“sp_executesql”,如:

    select b,a from table where b in(94,161,4,50,166,52,53,90,100,123)
    

    按预期显示“聚集索引搜索”

    为什么SQL Server会对第一个查询使用“索引扫描”?它与功能有关吗? “sp_executesql”本身?

    谢谢你 万斯

2 个答案:

答案 0 :(得分:1)

我已经看过我自己的数据库中类似查询的执行计划,可以看到差异,但我无法完全解释它;我只是觉得我的发现可能有用。

差异似乎是由于在编译的查询中使用了参数。

在下面的示例中,我使用的是来自我所拥有的数据库的[Resource]表,您必须更改查询的名称。

正如您发现在管理工作室中直接执行查询会导致索引搜索

T_SQL Query

使用带参数的版本进行扫描

enter image description here

如果您完全准备好语句,然后将其传递给数据库,例如

exec('select id from [Resource] where id in (1,5,7,9,10)')

你再次获得索引

enter image description here

有趣的是查看缓存的计划

SELECT cp.objtype,cp.usecounts,q.TEXT
FROM sys.dm_exec_cached_plans cp
 cross apply sys.dm_exec_query_plan(cp.plan_handle) p
 cross apply sys.dm_exec_sql_text(cp.plan_handle) AS q
WHERE cp.cacheobjtype = 'Compiled Plan'

我执行的三个语句中的哪个

objtype   usecounts text
-------   --------- ----
Adhoc     1         select id from [Resource] where id in (1,5,7,9,10)
Prepared  1         (@p1 int, @p2 int, @p3 int, @p4 int, @p5 int)select id from [Resource] where id in (@p1, @p2, @p3, @p4, @p5)
Adhoc     1         select id from [Resource] where id in (1,5,7,9,10)

正如您所看到的,SQL非常不同。不幸的是,就我所能解释你所看到的指数选择的差异而言。也许其他人可以更进一步?

编辑1:我已经阅读了更多内容,它归结为优化者必须创建一个计划来满足所有可能的参数值(正如Kragen在他的回答中所说)。

我在本文中找到了相同的信息:Dynamic Search Conditions in T-SQL

编辑2:回应Martin的评论,这是他的SQL语句的执行计划

enter image description here

答案 1 :(得分:1)

差异可能是由于在不同数据或不同参数意味着表扫描/索引搜索适当时执行的缓存查询计划。 (SQL命令文本本身不同,因此它们在计划缓存中都有不同的条目)。如果要对此进行测试,可以使用以下命令清除计划缓存:

DBCC FREEPROCCACHE -- Don't run me on a production SQL server!

然后尝试再次运行这两个命令,看看是否还有差异(如果您愿意挖掘计划缓存,上述命令的版本可以安全生产)

请注意,表扫描并不总是坏事 - 尤其是当表格很窄并且行数不多时(在您的示例中看起来就是这种情况)。在这种情况下,表扫描可以比索引搜索更有效。