Ada*_*dam 5 sql-server execution-plan sql-server-2014
我有下表
create table t1(
col1 varchar(255) NOT NULL,
col2 varchar(255) NULL,
col3 bigint NULL,
CONSTRAINT PK_t1 PRIMARY KEY CLUSTERED
(
col1 asc
)
)
CREATE NONCLUSTERED INDEX I_col3 ON t1(col3 desc)
Run Code Online (Sandbox Code Playgroud)
这个表有大约 10000 行,col2 总是被填充,col3 有不同百分比的非空行。
我正在运行以下查询
DECLARE @number bigint
SET @number = 123456
SELECT col1, col2 FROM t1
WHERE col3 > @number
Run Code Online (Sandbox Code Playgroud)
SQL 总是使用聚集索引扫描生成执行计划。
现在,如果我将查询作为临时查询运行,SQL 会使用键查找对 I_col3 进行索引查找
SELECT col1, col2 FROM t1
WHERE col3 > 123456
Run Code Online (Sandbox Code Playgroud)
WHERE 子句中传递的值会导致返回少量行(例如 3)
运行即席查询时,执行计划将估计行数显示为 3.32。但是,当我运行参数化查询时,它显示了 2796 行估计值。
如果我向临时查询添加索引提示,它仍会显示 2796 条估计行(不是我希望这会改变),但它确实会进行索引查找。在比较临时和参数化查询之间的逻辑读取数时,参数化查询:扫描计数 1,逻辑读取 89,物理读取 1,预读读取 64,lob 逻辑读取 0,lob 物理读取 0,lob 读取-提前读取 0。
Ad-Hoc 查询扫描计数 1,逻辑读取 8,物理读取 1,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。
并且当使用索引提示强制索引时,扫描计数 1,逻辑读 12,物理读 1,预读 18,lob 逻辑读 0,lob 物理读 0,lob 预读 0。
我目前正在运行 SQl 2014 CP1-CU5。我已经尝试使用跟踪标志 9184 来强制 SQL 使用旧的基数估计器,它将估计的行数减少到 1627,但仍然进行聚集索引扫描。我也试过 SQL 2008 R2,它的行为与 SQL 2014 相同,跟踪标志为 9184。
在查询的每次迭代之间,我正在清除过程缓存和缓冲池。
我还应该指出,我还尝试了以下
我还对聚集索引和非聚集索引进行了完全重建,并验证了统计数据是否是最新的。
有什么建议或想法吗?
再次感谢
更新:通过阅读 Martin 回答中的一些链接,我发现了以下博客文章:
http://www.sqlskills.com/blogs/kimberly/exec-and-sp_executesql-how-are-they-different/
我确认使用 sp_executesql 或 exec(@sql) 确实会导致生成并重用索引查找执行计划,而不管参数值是什么。
DECLARE @sql nvarchar(max)
SELECT @sql = 'SELECT col1, col2, FROM t1 WHERE col3 > @number'
EXEC sp_executesql @sql, N'@number bigint', @number = 123456789
Run Code Online (Sandbox Code Playgroud)
变量的值通常不会被嗅探,因此它只会假设表的 30% 将被返回,因为大于对未知值的谓词(参见没有 Statistics 的选择性猜测)。
当您使用文字时,它可以在列统计信息中查找已知值以获得更准确的估计。
如果它估计有如此高的表匹配百分比,则极不可能为您提供查找计划(确切的临界点取决于表和索引的宽度)。
但是,如果您使用option (recompile)
变量值可以被嗅探......
DECLARE @number bigint
SET @number = 123456
SELECT col1, col2
FROM t1
WHERE col3 > @number
OPTION (RECOMPILE);
Run Code Online (Sandbox Code Playgroud)
可能会为您提供与使用文字时相同的计划。