我正在使用 StackOverflow 转储来运行一些测试。
特别是,我正在查询这张表:
我创建了这个索引:
我正在运行以下查询(只是强制索引来测试替代方案)
我得到以下执行计划的成本很高(66.63)。
这些是运行此查询后的 IO 统计信息:
然后,我通过提供变量而不是直接值来运行相同的查询
我得到了一个更好的计划(成本是 0.4385)。
统计数据也更好:
起初...我认为 SQL Server 没有将直接值识别为 INT,但既没有类型不匹配也没有隐式转换警告。
我还尝试避免并行性,但在谓词中传递直接值时,我仍然会使用 MAXDOP 1 获得高成本计划(和更高的 IO 统计信息)。
在比较两个计划时,有不同的估计:
作为谓词的一部分传递直接值有什么问题?
变量和参数的区别
优化器在计算变量的估计值时使用统计密度向量。
当“直接”或“静态”值直接嵌入到查询中时,将使用统计直方图。这就是为什么你会得到不同的估计,从而得到不同的计划。
这是我的估计计划:https ://www.brentozar.com/pastetheplan/?id=SJCduTuKN
在我 2010 年的 SO 数据库副本中,OwnerUserId 列的密度为 0.000003807058。乘以 3,744,192 行 = 14.2544 行。这正是 IX_Posts_OwnerUserId 估计出来的行数。
您可以通过运行以下 DBCC 命令获取有关该索引的统计信息的信息:
这是(缩写)输出:
由于 PostTypeId 也是 WHERE 子句的一部分,因此也会自动为该列生成统计信息。该密度向量为 0.25 x 3,744,192 行 = 936,048 行。
和输出:
由于这是一个“AND”谓词,因此估计使用两者中的较低值。
当您使用静态值而不是变量时,它使用统计直方图。这是该 SHOW_STATISTICS 命令的第三个结果集中。对于您正在使用的键,这是直方图条目:
这就是“静态值”计划中 11,371 的估计值的来源。
直方图在很多时候可以是一个更好的估计,因为它可以更好地处理边缘情况——因为像这样的大表中经常会有一些异常值。
成本核算差异
在这种特定情况下,直方图会产生一个完全正确的估计。生成的计划的成本(正确地)高于使用密度向量的成本,因为它必须处理更多的行。
“低成本”计划认为该搜索将产生 14 行,而实际上产生了 11,371 行。
逻辑读取
由于嵌套循环预取,并行计划中的逻辑读取略高。在我的机器上似乎并没有太大的区别——查询的经过时间在 10 毫秒之内。
并行性实际上没有任何帮助,因为所有行最终都在一个线程上(无论如何在我的机器上)。添加
OPTION (MAXDOP 1)
有助于缩短执行时间,但不会删除额外的逻辑读取。此查询的“额外读取”问题的一个潜在解决方案是通过将 PostTypeId 添加为包含列来完全避免键查找: