SQL中的表扫描和索引扫描

Posted

技术标签:

【中文标题】SQL中的表扫描和索引扫描【英文标题】:Table Scan and Index Scan in SQL 【发布时间】:2012-01-31 22:33:43 【问题描述】:

SQL中的Table scan和Index scan有什么区别,具体用在什么地方?

【问题讨论】:

What's the difference between a Table Scan and a Clustered Index Scan?的可能重复 一个扫描表行,其他索引行。你用的是什么关系型数据库? 【参考方案1】:

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

例子:

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) 操作,但由于索引通常小于表,因此扫描索引所需的磁盘访问次数少于扫描表所需的磁盘访问次数。

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

【讨论】:

在您的示例中它将使用索引搜索(可能是范围搜索)而不是索引扫描。如果索引覆盖但不在有用的前导列上,或者谓词是选择性的并且索引比表窄,我希望进行索引扫描。 好的,我更正了我的答案,以考虑到索引扫描和索引搜索之间的区别。【参考方案2】:

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

索引扫描是指遍历所有索引项,当项索引满足搜索条件时,通过索引检索表行。

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

他们对这个问题有很多参考书目。示例:

微软:Which is Faster: Index Access or Table Scan?:

索引访问是一种访问方法,其中 SQL Server 使用现有的 索引来读取和写入数据页。因为索引访问显着 减少了 I/O 读取操作的数量,它通常优于 表扫描。

甲骨文:The Query Optimizer

在此方法中,通过遍历索引检索行,使用 语句指定的索引列值。索引扫描 根据一列或多列的值从索引中检索数据 在索引中。为了执行索引扫描,Oracle 在索引中搜索 语句访问的索引列值。如果声明 只访问索引的列,然后 Oracle 读取索引的 列值直接来自索引,而不是来自表。

mysql:How to Avoid Table Scans

【讨论】:

【参考方案3】:

@danihp 已经回答了问题的第一部分,我将尝试回答第二个“它具体在哪里使用”。这适用于 Oracle,但适用于大多数 RDBMS。

假设我们有一个表my_table,它在id 列上具有唯一索引,并且在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_table 中的所有内容,其中id = '1'

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

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

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

现在,在这些情况下,表扫描和索引扫描之间似乎没有太大区别。我们仍然需要在数据库中的对象中找到一个值。但是,由于索引要小得多并且专门设计用于扫描(请参阅其他答案),如果您只想要其中的一小部分行,通常执行索引扫描要快得多表。如果你想说表格的 10%,那么这一点就变成了“视情况而定”。

【讨论】:

Oracle 不区分查找和扫描吗? @MartinSmith,我从来没有听说过/见过他们被描述为“寻求”,但我明白你的意思。我已经编辑了我的答案以包含一个非 PK 示例。我猜唯一索引是seek,非唯一索引是scan【参考方案4】:

至少对于 SQL Server:

索引扫描可能更快,因为可能索引不会覆盖表中的整个列集,而表(或聚集索引)扫描必须读取所有数据。如果索引确实包含表中的所有列,那么它应该大致相当于表扫描,并且在索引扫描和表(或 CIX)扫描之间进行选择将是掷硬币。不同之处在于,当索引中的列数较少时,您可以在 8kb 页面上容纳更多的索引行,从而减少为扫描索引中的所有数据而必须读取的总页数。

为了说明我的意思,假设您有两份电话簿,一份包含姓氏、名字、街道地址和电话号码,另一份只有姓氏、名字和电话号码。现在想象一下,因为不必打印街道地址,您可以在电话簿的任何页面上放置两列额外的姓名和电话号码。这样做的最终结果是电话簿更薄,因为您可以在更少的页面上放置相同数量的电话号码。接下来,假设您负责计算书中电话号码的数量。你会选择哪一个,一个列出街道地址(它有更多的页面,类似于表扫描)或没有街道地址的一个(它有更少的页面,类似于大多数索引扫描)?我会选择页面较少的那个。

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

【讨论】:

以上是关于SQL中的表扫描和索引扫描的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server中的索引搜索与索引扫描[关闭]

索引的访问-SQL Server

避免全表扫描的sql优化

数据库全表扫描的SQL种类

mysql慢sql优化

SQL 数据优化索引建suo避免全表扫描