索引查找和索引扫描之间有什么区别?
哪个表现更好?
sql如何选择其中一个?
我意识到这是3个问题,但我认为回答第一个问题将解释其他问题.
解决方法
不太简短的版本:搜索通常要好得多,但是很多搜索(例如由于错误的查询设计和令人讨厌的相关子查询导致,或者因为你在游标操作或其他循环中进行了许多查询)可能比扫描,特别是如果您的查询可能最终从受影响的表中的大多数行返回数据.
它有助于覆盖整个系列的数据查找操作,以充分了解性能影响.
表扫描:由于根本没有与查询相关的索引,计划程序将被强制使用表扫描,这意味着每行都会被查看.这可能导致从磁盘读取与表数据相关的每个页面,这通常是最糟糕的情况.请注意,对于某些查询,即使存在有用的索引,它也会使用表扫描 – 这通常是因为表中的数据太小而不能遍历索引(如果是这种情况,您会期望假设指数的选择性度量很好,计划随着数据的增长而改变.
行查找的索引扫描:没有可以直接用于搜索的索引,但是存在包含右列的索引,可以使用索引扫描.例如,如果你有一个包含20列且在column1,col2,col3上有索引的大表,则发出SELECT col4 FROM exampletable WHERE col2 = 616,在这种情况下,扫描索引以查询col2比扫描整个表更好.一旦找到匹配的行,则需要读取数据页以拾取col4以进行输出(或进一步加入),这是在查询计划中看到它时的“书签查找”阶段.
没有行查找的索引扫描:如果上面的示例是SELECT col1,col3 FROM exampletable WHERE col2 = 616则不需要额外的读取数据页面的工作:一旦找到匹配col2 = 616的索引行,所有请求的数据都是已知的.这就是为什么你有时会看到永远不会被搜索的列,但可能会被请求输出,添加到索引的末尾 – 它可以保存行查找.出于这个原因而仅将此列添加到索引时,请使用INCLUDE子句添加列,以告知引擎它不需要根据这些列优化索引布局以进行查询(这可以加快对这些列所做的更新) ).索引扫描也可以来自没有过滤子句的查询:SELECT col2 FROM exampletable将扫描此示例索引而不是表页.
索引搜索(有或没有行查找):在搜索中,不考虑所有索引.对于查询SELECT * FROM exampletable WHERE c1 BETWEEN 1234 AND 4567,查询引擎可以通过对c1上的索引执行基于树的搜索来找到将匹配的第一行,然后它可以按顺序导航索引,直到它到达结尾范围(这与c1 = 1234的查询相同,因为即使对于=操作,也可能有许多行匹配条件).这意味着只需要读取相关索引页(加上初始搜索所需的几个)而不是索引(或表)中的每个页面.
聚簇索引:使用聚簇索引,表数据存储在该索引的叶节点中,而不是存储在单独的堆结构中.这意味着在使用该索引查找行之后永远不需要进行任何额外的行查找,无论需要哪些列[除非您有包含长数据的TEXT列或VARCHAR(MAX)列等页外数据].
出于这个原因,你只能有一个聚簇索引[1],聚集索引是你的表而不是一个单独的堆结构,所以如果你使用一个[2]选择你小心放置的地方,以获得最大的收益.
另请注意,聚簇索引因为表的“聚类键”而被包含在表的每个非聚簇索引中,所以宽聚簇索引通常不是一个好主意.
[1]实际上,通过定义覆盖或包含表中每一列的非聚簇索引,您可以有效地拥有多个聚簇索引,但这可能会浪费空间会影响写入性能,因此如果您考虑这样做,请确保你真的需要.
[2]当我说“如果你使用聚集索引”时,请注意通常建议你在每个表上都有一个.除了所有经验法则之外,还有一些例外,除了批量插入和无序读取(可能是ETL进程的登台表)之外,其他表只是最常见的反例.
附加要点:不完整的扫描:
重要的是要记住,根据查询的其余部分,表/索引扫描可能实际上不扫描整个表 – 如果逻辑允许查询计划可能能够使其提前中止.最简单的例子是SELECT TOP(1)* FROM HugeTable – 如果你查看查询计划,你会看到只有一行从扫描中返回,如果你看IO统计数据(SET STATISTICS IO ON; SELECT TOP(1)* FROM HugeTable)你会发现它只读取了很少的页面(可能只有一页).
如果WHERE或JOIN … ON子句的谓词可以与作为其数据的源的扫描同时运行,则会发生同样的情况.查询规划器/运行器有时可以非常聪明地将谓词推回到数据源以允许以这种方式提前终止扫描(有时您可以聪明地重新排列查询以帮助它这样做!).当数据按照标准查询计划显示中的箭头从右向左流动时,逻辑从左向右运行,并且每个步骤(从右到左)不一定在下一个步骤开始之前运行完成.在上面的简单示例中,如果您将查询计划中的每个块视为代理,SELECT代理会向TOP代理询问一行,而该行反过来要求TABLE SCAN代理为一个,然后SELECT代理要求另一个但TOP代理知道没有必要甚至不打扰甚至询问表读者,SELECT代理得到“不再相关”的响应,并且知道所有的工作都已完成.许多操作当然会阻止这种优化,因此在更复杂的示例中,表/索引扫描确实会读取每一行,但请注意不要跳到任何扫描必须是昂贵操作的结论.