我有一个简单的日期表(Date,DateID),其中包含 1900 年 1 月 1 日到 2100 年 12 月 31 日之间的日期列表。
当使用从表中选择时between
运算符和硬编码参数值,我得到了一个正确的查询计划,其中有 3 个估计行与 2 个实际行相比:
select v.Date from Dates v
where v.Date between '20130128' and '20130129';
然而,当用参数替换硬编码值时,查询计划会变成一个非常糟糕的计划,估计有超过 6000 行,而实际只有 2 行:
select v.Date from Dates v
where v.Date between @startdate and @enddate;
查询计划本身是相同的,只是估计行数的差异导致参数化查询的运行速度比硬编码查询慢大约 4 倍。对于参数化版本运行速度如此慢的原因,我是否遗漏了什么,以及我可以为 SQL Server 提供哪些索引/提示来帮助它使用正确的查询计划?
一些附加信息:
- 使用简单相等时不会出现问题
=
标准,似乎特定于between
操作员。
- 如果我添加
option(recompile)
在参数化查询结束时,我得到了一个完美的查询计划,与硬编码查询相同。
- 日期表只有两列:Date 和 DateID,主键 DateID 列上有一个聚集索引,Date 列上有一个唯一的非聚集索引。所有这些都已更新统计数据。
- 查询计划对硬编码查询执行自动参数化,用@1和@2替换硬编码值,并将查询显示为大写。它似乎没有对参数化查询执行任何转换。
- 使用 SQL Server 2008 R2。
我知道的足够意识到suspect这是某种参数嗅探问题。为什么不添加option(recompile)
到查询?这被用作一个更大的复杂查询的一部分,我知道好的做法是让 SQL Server 做它的事情,并在可能的情况下重用缓存中的查询计划。
编辑和更新:感谢迄今为止的深思熟虑的回复。为了进一步细化问题,查询计划对上述两个查询都使用了一个完美的索引,但是为什么它没有认识到参数化查询的日期范围只有两天,为什么它认为范围是 6000行宽?尤其是当查看查询计划时,SQL Server 无论如何都会为硬编码查询执行自动参数化?在底层查询计划中,两个计划看起来相同,因为它们都是参数化的!
查询计划基于首次运行查询时的参数值。这就是所谓的参数嗅探 http://www.sommarskog.se/query-plan-mysteries.html。当你添加option (recompile)
,每次执行都会生成一个新计划。
查询计划根据 SQL 查询的哈希进行缓存。因此,两个版本的查询都有不同的缓存槽。
Adding option (recompile)
是一个很好的解决方案。您还可以使用:
option (optimize for (@startdate = '20130128', @enddate = '20130129'));
生成查询计划,就好像这些值已传入一样。
为了进行测试,您可以使用以下命令从缓存中删除所有计划:
DBCC FREEPROCCACHE
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)