繁体   English   中英

SQL中的表扫描和索引扫描

[英]Table Scan and Index Scan in SQL

SQL中的表扫描和索引扫描之间有什么区别?具体用途是什么?

表扫描意味着遍历所有表行。

索引扫描意味着遍历所有索引项,当项索引满足搜索条件时,将通过索引检索表行。

通常,索引扫描比表扫描便宜,因为索引比表更平坦。

关于这个问题,他们有很多书目。 样品:

索引访问是一种访问方法,其中SQL Server使用现有索引来读取和写入数据页。 因为索引访问会大大减少I / O读取操作的次数,所以它通常胜过表扫描。

在此方法中,使用语句指定的索引列值通过遍历索引来检索行。 索引扫描基于索引中一或多个列的值从索引中检索数据。 为了执行索引扫描,Oracle在索引中搜索该语句访问的索引列值。 如果该语句仅访问索引的列,则Oracle直接从索引而不是从表读取索引的列值。

大多数查询引擎都有一个查询优化器,它会尝试生成有效的查询执行策略。 如果索引可用,这可以使查询更快,则查询优化器将执行索引扫描或索引查找,否则执行表扫描。

例:

SELECT * FROM tbl WHERE category_id = 5;

如果category_id上没有索引,则将执行表扫描,即,将检查表中的每个记录以获取正确的category_id。

但是,如果为category_id编制了索引,则情况将变得更加复杂。 如果表很大,则可能会选择索引查找。 但是,如果表很小,则优化器可能会决定表扫描仍然更快,因为访问索引需要一些开销。 如果category_id的选择性不够好(例如,如果只有两个类别),则即使对于大型表,扫描表的速度也可能更快。

索引通常组织为树结构。 在树中查找项目是O(log n)操作。 表扫描是O(n)操作。 速度主要取决于执行查询所需的磁盘访问次数。 首先查找索引,然后访问表中的已找到条目可以为小型表生成更多磁盘访问。

让我们看一下另一个查询:

SELECT category_id FROM tbl WHERE category_id BETWEEN 10 AND 100;

这里有另一个选择。 在这种情况下,索引查找可能不会比表扫描更快,但是由于我们仅检索catergory_id,因此索引扫描(而非索引查找)可能甚至更快。 索引扫描读取索引表的每个条目,而不是利用树结构(索引查找的作用)。 但是,由于所请求的信息完全包含在索引中,因此不需要访问数据表。 索引扫描类似于表扫描的O(n)操作,但是由于索引通常小于表,因此与扫描表相比,扫描索引所需的磁盘访问次数更少。

整个问题非常复杂,并且在很大程度上取决于数据库引擎。 如果您想了解更多信息,请阅读数据库供应商提供的文档。

至少对于SQL Server:

索引扫描可以更快,因为假定索引不能覆盖表中的整个列,而表(或聚集索引)扫描则必须读取所有数据。 如果索引确实包含表中的所有列,则它应大致等同于表扫描,并且在索引扫描和表(或CIX)扫描之间进行选择将是一次抛硬币。 区别在于,当索引中的列较少时,可以在8kb页面上容纳更多的索引行,从而减少了要扫描索引中的所有数据而需要读取的总页数。

为了说明我的意思,请想象您是否有电话簿的两个副本,一个带有姓氏,名字,街道地址和电话号码,一个只有姓氏,名字和电话号码。 现在想象一下,由于不必打印街道地址,因此您可以在电话簿的任何页面上增加两列名称和电话号码。 最终结果是电话簿更薄,因为您可以在更少的页面上放入相同数量的电话号码。 接下来,假设您负责计算书中的电话号码数量。 您会选择哪个,列出地址的街道地址(页数多,类似于表扫描),或者不列出街道地址的地址(页数少,与大多数索引扫描类似)? 我会选择页数较少的那张。

另一个缺点是可以过滤某些索引,这意味着它们不仅在大多数情况下具有较少的列(因此可以在单个页面上容纳更多的行),而且还可以具有WHERE子句,从而消除了很多行。 同样,在这种情况下,索引扫描将比表扫描更好(但这仅适用于具有匹配的WHERE子句和相同语义的查询)。

@danihp回答了问题的第一部分后,我将尝试回答第二个“具体用于何处” 这适用于Oracle,但对于大多数RDBMS都适用。

假设我们有一个表my_table ,该表在列id上唯一地索引,并且在yet_another_column列上有yet_another_column唯一yet_another_column

create my_table ( id varchar2(20) not null
                , another_column not null
                , yet_another_column
                , constraint pk_my_table primary key (id) 
                );

create index i_my_table on my_table ( yet_another_column );

现在,如果我们select * from my_table where id = '1'它将/应该对索引pk_my_table进行唯一的索引扫描 然后,我们使用索引重新输入表,以返回my_tableid = '1'

如果查询是select id from my_table where id = 'a'则不需要第二阶段,因为我们需要的所有值都包含在索引中。 在这种情况下,查询将仅执行唯一索引扫描

接下来,如果我们的查询select * from my_table where yet_another_column = 'y'那么我们在该列上有一个索引,但是它不是唯一的,因此我们将不得不遍历整个索引以尝试找到所有匹配的值我们的条件,即索引扫描 再一次,我们选择不在索引中的列,因此我们必须重新输入表才能获取它们。

最后,如果我们的查询是select id from my_table where another_column = 'yes' 我们在another_column上没有索引,因此我们必须进行表扫描以找到该值,即,我们必须在表中where another_column = 'yes'

现在,在这些实例中的表扫描和索引扫描之间似乎没有太大区别。 我们仍然必须去数据库中的对象中查找值。 但是,由于索引要小得多,并且是专门为扫描而设计的(请参见其他答案),因此, 如果只希望表中的行的比例很小 ,则进行索引扫描通常会快得多。 如果要说表的10%,那么这一点就变成“取决于”。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM