Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 为什么有时表扫描比索引扫描快?_Sql_Sql Server_Indexing_Query Optimization_Sql Execution Plan - Fatal编程技术网

Sql 为什么有时表扫描比索引扫描快?

Sql 为什么有时表扫描比索引扫描快?,sql,sql-server,indexing,query-optimization,sql-execution-plan,Sql,Sql Server,Indexing,Query Optimization,Sql Execution Plan,我创建了一个表来测试正在读取的逻辑块的数量和查询优化器选择的执行计划,比较该表有索引和没有索引时的查询 测试台是 create table scan ( id int identity(1, 1), a varchar(10), b varchar(10), c varchar(10), d varchar(10), e varchar(10), f varchar(10) ) 当我运行这些查

我创建了一个表来测试正在读取的逻辑块的数量和查询优化器选择的执行计划,比较该表有索引和没有索引时的查询

测试台是

 create table scan
 ( 
      id int identity(1, 1), 
      a varchar(10), 
      b varchar(10),
      c varchar(10), 
      d varchar(10), 
      e varchar(10), 
      f varchar(10) 
)
当我运行这些查询时:

select * from scan
select id from scan
select * from scan
select id from scan
我得到了88次和58次逻辑读取,还有一个表扫描算法

然后我修改表,放入pk约束和他的集群id

alter table scan
add constraint fk_id primary key (id)
然后运行相同的查询:

select * from scan
select id from scan
select * from scan
select id from scan
我得到了90次和60次扫描以及索引扫描算法


问题是:如果查询优化器选择了运行查询的最佳方式,那么如果表扫描可以读取更少的块,为什么它会选择索引扫描?

当您创建主键约束时,默认情况下它是聚集的。这意味着您以前使用的堆已按此键排序(
id
,在您的情况下)

表中的数据可以以两种方式之一存储:堆或聚集索引。当后者被创建时,前者就消失了。所以SQL Server不可能对索引执行表扫描-它只能是索引扫描(顺便说一句,聚集索引扫描-这很重要)


我知道这听起来可能令人困惑,但试着阅读一些关于聚集索引的基础知识——这可能会有所帮助。

你只是在阅读所有的行——索引没有做任何事情。添加WHERE子句并查看结果。查询优化器预测运行查询的最佳方式。如果不检查数据,It通常不可能知道最佳方法,这将破坏目的。因此,它不会总是选择绝对最好的策略,但它应该非常可靠地选择一个非常好的策略。您的数据并不表明存在其他情况。您的数字是实际读数还是估计读数?在这么小的表上,索引不太可能特别有用。@GordonLinoff,这些数字是我使用SET STATISTICS IO On得到的。在上面的答案中检查我的评论。这是我对这些数字的怀疑,但我仍然不知道为什么它用索引读取的块比用表扫描读取的块多。如果没有索引,我有7列,假设每个列有1kb,而块是14kb,那么它每个块读取2行。对于索引,我们有两列,一列用于索引,另一列用于指向行地址的指针。如果您的每个块也有1kb,则块know将有7行。所以我用更少的块阅读更多的信息。我认为,在SELECT*的情况下,它应该比表扫描读取更少的块,或者比表扫描读取更多的块,因为它将读取“索引表”和真实的表。@Mucida,heap在物理上实现为一个双链接列表,其中每个表页都有两个指针——在堆的上一页和下一页。聚集索引是以B树的形式实现的,因此它为根级和中间级页面消耗了稍多的空间(平均约为表大小的1%)。根据表是否为堆,将使用表扫描(即堆扫描)或聚集索引扫描。对于同一个表,磁盘上不能同时有两个结构——毕竟只有一个表。是的,查询优化程序使用一组有点神秘的算法来确定查询最终将检索到的表的很大一部分。随着
所选页面/总页面的比率降低(通过增加表大小或缩小搜索条件),在某些时候它开始倾向于搜索而不是扫描。@Mucida,因为在某些时候它决定使用非聚集索引快速定位行,然后执行一些RID查找会更便宜(相当贵)而不是扫描整个堆表。这都是关于成本的。@Mucida,是的,它就是这样工作的。这个等式中还有其他几个标准,但通常都是关于IO的。顺便说一句,您可以尝试使用提示强制执行某些特定策略,并比较结果执行计划及其有效性。除非统计数据过时或者索引碎片化得很厉害,查询优化器很少会忽略这一点。