10

SQL中的表扫描和索引扫描有什么区别,具体用在哪里?

4

4 回答 4

15

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

例子:

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 供应商提供的文档。

于 2012-01-02T16:27:52.083 回答
15

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

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

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

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

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

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

于 2012-01-02T16:14:14.713 回答
2

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

假设我们有一个 table 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'这样做将/应该对 index 进行唯一的索引扫描pk_my_table。然后我们使用索引重新进入表,返回my_tablewhere 中的所有内容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%,那么这一点就变成了“取决于”。

于 2012-01-02T16:39:02.970 回答
2

至少对于 SQL Server:

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

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

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

于 2013-12-06T14:56:55.660 回答