SQL中的表扫描和索引扫描有什么区别,具体用在哪里?
4 回答
大多数查询引擎都有一个查询优化器,它试图生成有效的查询执行策略。如果索引可用,这可以使查询更快,那么查询优化器将执行索引扫描或索引查找,否则执行表扫描。
例子:
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 供应商提供的文档。
表扫描意味着遍历所有表行。
索引扫描是指遍历所有索引项,当项索引满足搜索条件时,通过索引检索表行。
通常索引扫描比表扫描便宜,因为索引比表更平坦。
他们对这个问题有很多参考书目。样本:
- 微软:哪个更快:索引访问还是表扫描?:
索引访问是一种访问方法,其中 SQL Server 使用现有索引来读取和写入数据页。因为索引访问显着减少了 I/O 读取操作的数量,所以它通常优于表扫描。
- Oracle:查询优化器
在此方法中,通过使用语句指定的索引列值遍历索引来检索行。索引扫描根据索引中一个或多个列的值从索引中检索数据。为了执行索引扫描,Oracle 在索引中搜索语句访问的索引列值。如果语句只访问索引的列,则 Oracle 直接从索引中读取索引列的值,而不是从表中读取。
- MySql:如何避免表扫描
正如@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_table
where 中的所有内容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%,那么这一点就变成了“取决于”。
至少对于 SQL Server:
索引扫描可以更快,因为可能索引不会覆盖表中的整个列集,而表(或聚集索引)扫描必须读取所有数据。如果索引确实包含表中的所有列,那么它应该大致相当于表扫描,并且在索引扫描和表(或 CIX)扫描之间的选择将是掷硬币。不同之处在于,当索引中的列较少时,可以在 8kb 页面上容纳更多索引行,从而减少为扫描索引中的所有数据而必须读取的整体页面。
为了说明我的意思,想象一下如果你有两份电话簿,一份有姓氏、名字、街道地址和电话号码,另一份只有姓氏、名字和电话号码。现在想象一下,因为不必打印街道地址,您可以在电话簿的任何页面上添加两列姓名和电话号码。这样做的最终结果是电话簿更薄,因为您可以在更少的页面上放置相同数量的电话号码。接下来,假设您负责计算书中电话号码的数量。您会选择哪一个,列出街道地址的一个(具有更多页面,类似于表扫描)或没有街道地址的一个(具有更少页面,类似于大多数索引扫描)?我会选择页数较少的那个。
另一个问题是某些索引可以被过滤,这意味着它们不仅在大多数情况下具有更少的列(因此可以在单个页面上容纳更多行),而且它们还可以有一个 WHERE 子句来消除很多行。在这种情况下,索引扫描也比表扫描更好(但这仅适用于具有匹配 WHERE 子句和相同语义的查询)。