如何充分利用索引

SQL索引优化技巧
本文介绍了SQL索引的建立及使用技巧,包括何时建立索引、如何高效利用索引进行查询,以及如何优化查询语句以减少系统开销。通过遵循这些规则,可以显著提高数据库查询性能。

转载:http://hi.baidu.com/yfdong21/blog/item/3acebe8980023fbd0f244401.html

一、什么情况下适合建立索引?

 

每个表只能有一个聚集索引

关联字段上应该建立索引

在条件表达式中经常用到的不同值较多的列上建立检索,在不同值少的列上不要建立索引

 

多条件查询时,联合索引效率较高。

经常同时存取多列,且每列都含有重复值可考虑建立组合索引

书签查找的原因是使用了SELECT *。由于在绝大多情况下它会返回比你实际所需更多的数据,所以应该

永不使用SELECT *.

 

二、什么情况下会使用索引查询?

 

1、Like语句是否属于SARG取决于使用%通配符的样式,通配符%在字符串首字符的使用会导致索引无法使用

例如:name like ‘张%’ ,这就属于SARG,而:name like ‘%张’ ,就不属于SARG


2、 “非”操作符不满足SARG形式,使得索引无法使用。

不满足SARG形式的语句最典型的情况就是包括非操作符的语句,如:NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE等。

例如:select * from employee where salary<>3000; 

select * from employee where salary<3000 or salary>3000; 

第二种查询允许对salary列使用索引,而第一种查询则不能使用索引。


3、 函数运算不满足SARG形式,使得索引无法使用

例:下列SQL条件语句中的列都建有恰当的索引,但执行速度却非常慢:

select * from record where substring(card_no,1,4)=′5378′(13秒)

select * from record where amount/30< 1000(11秒)

select * from record where convert(char(10),date,112)=′19991201′(10秒)

分析: 

where子句中对列的任何操作结果都是在SQL运行时逐列计算得到的,因此它不得不进行全表扫描,而没有使用该列上面的索引;如果这些结果在查询编译时就能得到,那么就可以被SQL优化器优化,使用索引,避免表搜索,因此将SQL重写成下面这样:

select * from record where card_no like ′5378%′(< 1秒) 

select * from record where amount < 1000*30(< 1秒) 

select * from record where date= ′1999/12/01′ (< 1秒)


4、 尽量不要对建立了索引的字段,作任何的直接处理

select * from employs where first_name + last_name ='beill cliton'; 无法使用索引


select * from employee where first_name = substr('beill cliton',1,instr('beill cliton',' ')-1) 

and last_name = substr('beill cliton',instr('beill cliton',' ')+1) 则可以使用索引


5、 不同类型的索引效能是不一样的,应尽可能先使用效能高的

比如:数字类型的索引查找效率高于字符串类型,定长字符串char,nchar的索引效率高于变长字符串varchar,nvarchar的索引。

应该将 where username='张三' and age>20

改进为 where age>20 and username='张三'

注意:此处,SQL的查询分析优化功能可以做到自动重排条件顺序,但还是建议预先手工排列好。


6、 尽量不要使用 is null 与 is not null作为查询条件

任何包含null值的列都将不会被包含在索引中,如果某列数据中存在空值,那么对该列建立索引的性能提升是值得怀疑的,

尤其是将null作为查询条件的一部分时。建议一方面避免使用is null和is not null, 另一方面不要让数据库字段中存在null, 即使没有内容,也应利用缺省值,或者手动的填入一个值,如:’’ 空字符串。


7、 某些情况下IN 的作用与OR 相当 ,且都不能充分利用索引

例:表stuff有200000行,id_no上有非群集索引,请看下面这个SQL:

select count(*) from stuff where id_no in(′0′,′1′) (23秒)

where条件中的′in′在逻辑上相当于′or′,所以语法分析器会将in (′0′,′1′)转化为id_no =′0′ or id_no=′1′来执行。

我们期望它会根据每个or子句分别查找,再将结果相加,这样可以利用id_no上的索引;但实际上,它却采用了"OR策略",

即先取出满足每个or子句的行,存入临时数据库的工作表中,再建立唯一索引以去掉重复行,最后从这个临时表中计算结果。因此,实际过程没有利用id_no上索引,并且完成时间还要受tempdb数据库性能的影响。 

实践证明,表的行数越多,工作表的性能就越差,当stuff有620000行时,执行时间会非常长!如果确定不同的条件不会产生大量重复值,还不如将or子句分开:

select count(*) from stuff where id_no=′0′ 

select count(*) from stuff where id_no=′1′

得到两个结果,再用union作一次加法合算。因为每句都使用了索引,执行时间会比较短, 

select count(*) from stuff where id_no=′0′ 

union

select count(*) from stuff where id_no=′1′

从实践效果来看,使用union在通常情况下比用or的效率要高的多,而exist关键字和in关键字在用法上类似,性能上也类似,都会产生全表扫描,效率比较低下,根据未经验证的说法,exist可能比in要快些。


8、 使用变通的方法提高查询效率

  like关键字支持通配符匹配,但这种匹配特别耗时。例如:select * from customer where zipcode like “21_ _ _”,

即使在zipcode字段上已建立了索引,在这种情况下也可能还是采用全表扫描方式。

如果把语句改为:select * from customer where zipcode >“21000”,在执行查询时就会利用索引,大大提高速度。

但这种变通是有限制的,不应引起业务意义上的损失,对于邮政编码而言,zipcode like “21_ _ _” 和 zipcode >“21000” 意义是完全一致的。


9、 组合索引的高效使用

假设已在date,place,amount三个字段上建立了组合索引

select count(*) from record where date > ′19991201′ and date < ′19991214′ and amount > 2000 (< 1秒) 

select date,sum(amount) from record group by date (11秒) 

select count(*) from record where date > ′19990901′ and place in (′BJ′,′SH′)(< 1秒)

这是一个设置较合理的组合索引。它将date作为前导列,使每个SQL都可以利用索引,并且在第一和第三个SQL中形成了索引覆盖,因而性能达到了最优。如果索引不便于更改,修正SQL中的条件顺序以配合索引顺序也是可行的。


10、 order by按聚集索引列排序效率最高 

排序是较耗时的操作,应尽量简化或避免对大型表进行排序,如缩小排序的列的范围,只在有索引的列上排序等等。

我们来看:(gid是主键,fariqi是聚合索引列)

select top 10000 gid,fariqi,reader,title from tgongwen

用时:196 毫秒。 扫描计数 1,逻辑读 289 次,物理读 1 次,预读 1527 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by gid asc

用时:4720毫秒。 扫描计数 1,逻辑读 41956 次,物理读 0 次,预读 1287 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc

用时:4736毫秒。 扫描计数 1,逻辑读 55350 次,物理读 10 次,预读 775 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi asc

用时:173毫秒。 扫描计数 1,逻辑读 290 次,物理读 0 次,预读 0 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi desc

用时:156毫秒。 扫描计数 1,逻辑读 289 次,物理读 0 次,预读 0 次。

从以上我们可以看出,不排序的速度以及逻辑读次数都是和“order by 聚集索引列” 的速度是相当的,但这些都比“order by 非聚集索引列”的查询速度是快得多的。

同时,按照某个字段进行排序的时候,无论是正序还是倒序,速度是基本相当的。

 

三、索引的使用规则

 

联合索引使用总结:

1、查询条件中出现联合索引第一列,或者全部,则能利用联合索引.

2、条件列中只要条件相连在一起,以本文例子来说就是:

3、查询条件中没有出现联合索引的第一列,而出现联合索引的第二列,或者第三列,都不会利用联合索引查询.

 

单一列索引的应用总结:

1、只要条件列中出现索引列,无论在什么位置,都能利用索引查询.

 

两者的共同点:

1、要想利用索引,都要符合SARG标准.

2、都是为了提高查询速度.

3、都需要额外的系统开销,磁盘空间.

 

两者的区别:

 

1、索引所占用空间:单一列索引相对要小.

2、索引创建时间:单一列索引相对短.

3、索引对insert,update,delete的影响程序:单一列索引要相对低.

4、在多条件查询时,联合索引效率要高.

 

四、关于节省数据查询系统开销方面的措施

 

1、 使用TOP尽量减少取出的数据量,TOP是SQL SERVER中用来提取前几条或前某个百分比数据的关键词。

select top 20 gid,fariqi,reader,title from tgongwen order by gid desc

select top 60 percent gid,fariqi,reader,title from tgongwen order by gid desc

在实际的应用中,应该经常利用top 剔除掉不必要的数据,只保留必须的数据集合。这样不仅可以减少数据库逻辑读的次数,还能避免不必要的内存浪费,对系统性能的提升都是有好处的。

 

2、 字段提取要按照“需多少、提多少”的原则,避免“select *”

例:

select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc  用时:4673毫秒

select top 10000 gid,fariqi,title from tgongwen order by gid desc  用时:1376毫秒

select top 10000 gid,fariqi from tgongwen order by gid desc  用时:80毫秒

由此看来,字段大小越大,数目越多,select所耗费的资源就越多,比如取int类型的字段就会比取char的快很多。我们每少提取一个字段,数据的提取速度就会有相应的提升。提升的幅度根据舍弃的字段的大小来判断。

 

3、 count(*) 与 count(字段) 方法比较

我们来看一些实验例子(gid为Tgongwen的主键):

select count(*) from Tgongwen   用时:1500毫秒

select count(gid) from Tgongwen  用时:1483毫秒

select count(fariqi) from Tgongwen 用时:3140毫秒

select count(title) from Tgongwen 用时:52050毫秒

从以上可以看出,用count(*)和用count(主键)的速度是相当的,而count(*)却比其他任何除主键以外的字段汇总速度要快,而且字段越长,汇总速度就越慢。如果用count(*), SQL SERVER会自动查找最小字段来汇总。当然,如果您直接写count(主键)将会来的更直接些。

 

4、 有嵌套查询时,尽可能在内层过滤掉数据

如果一个列同时在主查询和where子句中出现,很可能当主查询中的列值改变之后,子查询必须重新查询一次。而且查询嵌套层次越多,效率越低,因此应当尽量避免子查询。如果子查询不可避免,那么要在子查询中过滤掉尽可能多的行。 

5、 多表关联查询时,需注意表顺序,并尽可能早的过滤掉数据

在使用Join进行多表关联查询时候,应该使用系统开销最小的方案。连接条件要充份考虑带有索引的表、行数多的表,并注意优化表顺序;说的简单一点,就是尽可能早的将之后要做关联的数据量降下来。

一般情况下,sqlserver 会对表的连接作出自动优化。例如: 

select name,no from A 

join B on A. id=B.id 

join C on C.id=A.id 

where name='wang' 

尽管A表在From中先列出,然后才是B,最后才是C。但sql server可能会首先使用c表。它的选择原则是相对于该查询限制为单行或少数几行,就可以减少在其他表中查找的总数据量。绝大多数情况下,sql server 会作出最优的选择,但如果你发觉某个复杂的联结查询速度比预计的要慢,就可以使用SET FORCEPLAN语句强制sql server按照表出现顺序使用表。如上例加上:SET FORCEPLAN ON…….SET FORCEPLAN OFF 表的执行顺序将会按照你所写的顺序执行。在查询分析器中查看2种执行效率,从而选择表的连接顺序。SET FORCEPLAN的缺点是只能在存储过程中使用。

 

 

 

  • 聚集索引比较宝贵,应该用在查询频率最高的地方;
  • 在数据为“既不是绝大多数相同,也不是极少数相同”状态时,最能发挥聚集索引的潜力。
  • 复合索引的设置和使用要注意保持顺序一致;
  • 条件子句的表达式最好符合SARG规范,是可利用索引的;
  • 任何对列的操作都导致全表扫描,如数据库函数、计算表达式等,
  • 查询时应尽可能将操作移至等号的某一边; 
  • 要注意含有null值时,是不能充分利用索引的;
  • exist, in、or等子句常会使索引失效;
  • 如果不产生大量重复值,可以考虑把子句拆开,再用union拼合;
  • 排序时应充分利用带索引的字段;
  • 尽可能早,快的过滤掉无用的数据,只将必须的数据带到后续的操作中去

 

 

<think> 首先,用户的问题是:“我想了解如何利用索引扫描进行排序 请问利用索引扫描实现排序的方法”,意思是“我想了解如何利用索引扫描进行排序,请问利用索引扫描实现排序的方法”。这涉及到数据库优化中的索引排序。 从系统级指令中,我需要: - 所有行内数学表达式用 $...$ 格式(但这里可能没有数学表达式)。 - 独立公式用 $$...$$ 格式(如果有)。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 - 参考提供的引用内容。 提供的引用内容: - [^1]: MySQL有两种方式生成有序结果:通过排序操作或索引顺序扫描。EXPLAIN中type="index"表示使用索引扫描排序。(不要和Extra="Using index"混淆) - [^2]: 解析SQLite索引扫描示例,包括创建表、索引和EXPLAIN QUERY PLAN。 - [^3]: MySQL中利用索引排序的基础教程。两种方式:filesort或索引顺序扫描。索引排序快,可以利用同一索引同时查找和排序。条件是ORDER BY列顺序与索引相同,且所有列方向一致;如果是连接查询,ORDER BY所有列必须是第一个表的列。 - [^4]: MySQL覆盖索引索引跳跃扫描。提到索引跳跃扫描是优化方案,但不是一定发生。 - [^5]: 类似[^3],显示EXPLAIN输出使用索引。 用户的问题聚焦在“利用索引扫描实现排序的方法”,所以我需要基于这些引用构建回答。 关键点从引用中提取: - 索引扫描排序:当ORDER BY的列顺序与索引相同,方向一致(全部升序或全部降序),数据库可能使用索引顺序扫描来排序,避免额外的排序操作。 - 适用于MySQLSQLite等数据库。 - EXPLAIN输出中type="index"表示使用索引扫描排序。 - 条件:索引覆盖ORDER BY的列;如果是多表连接,ORDER BY列必须是驱动表中的列。 - 索引跳跃扫描可能用于优化某些缺失前缀的索引。 回答结构: - 清晰、逐步解释如何利用索引扫描实现排序。 - 使用中文。 - 自然地添加引用标识。 - 结尾生成相关问题。 大纲回答: 1. **介绍索引扫描排序的概念**:解释什么是索引扫描排序及其优点(快速,节省资源)。 2. **实现条件**:列出利用索引扫描排序的前提条件。 - ORDER BY列顺序与索引相同。 - 所有ORDER BY列方向一致。 - 索引覆盖所有ORDER BY列(如果是覆盖索引更好)。 - 对于多表查询的限制。 3. **如何验证**:使用EXPLAIN命令检查是否使用索引扫描排序。 4. **示例**:提供一个简单的SQL示例来演示。 5. **注意事项**:提到索引跳跃扫描等优化,但强调成本考虑。 6. **生成相关问题**:结尾添加相关问题。 添加引用:在每个相关点后添加[^n]。 生成相关问题: - 类似示例:快速排序的问题是关于性能分析、优化、应用。 - 这里可以问:索引扫描排序的性能收益? 如何设计索引来优化排序? 索引扫描排序的限制? 最终回答格式: - 中文回答。 - 结构化。 - 末尾有“相关问题”。 </think> ### 利用索引扫描实现排序的方法 在数据库查询中,利用索引扫描实现排序是一种高效的优化手段。它通过直接读取索引的顺序来避免额外的排序操作(如filesort),从而显著提升查询性能。以下我将逐步解释实现方法、关键条件和注意事项,基于MySQL等常见数据库系统(如SQLite也有类似机制)。实现核心是确保索引的顺序与ORDER BY子句完全匹配。 #### 1. **索引扫描排序的基本原理** - 数据库存储索引时,索引键值本身就是有序的(如B+树结构)。当查询中的ORDER BY条件与索引的顺序一致时,数据库可以直接按索引顺序扫描数据,而无需单独执行排序操作[^1]。 - **优点**:速度快(时间复杂度接近 $O(n)$),并能减少内存和CPU开销,因为索引扫描可以同时处理查找和排序[^3]。 - **验证方法**:在执行查询前使用EXPLAIN命令,查看输出结果。如果type列的值为"index",表示使用索引扫描排序(注意:不要与Extra列的"Using index"混淆,后者仅表示覆盖索引)[^1][^5]。 #### 2. **实现索引扫描排序的关键条件** 要实现索引扫描排序,ORDER BY子句必须满足以下条件。如果不满足,数据库会退化为filesort(外部排序),效率更低。 - **索引覆盖ORDER BY列**:ORDER BY中的所有列必须包含在同一索引中,且索引顺序(从左到右)必须与ORDER BY列顺序完全一致。 - 示例:如果索引是idx_users_age(基于age字段),那么ORDER BY age可以触发索引扫描排序[^2]。 - 如果索引是复合索引(如idx_users_age_name),ORDER BY age, name才能使用索引扫描;ORDER BY name, age则不匹配[^3]。 - **列方向一致**:ORDER BY所有列必须是同一排序方向(全部升序或全部降序)。混合方向(如ORDER BY age ASC, name DESC)会禁用索引扫描[^3]。 - **查询类型限制**: - 对于单表查询:索引必须覆盖WHERE和ORDER BY的所有相关列。 - 对于多表连接(JOIN):ORDER BY中的所有列必须来自驱动表(第一个表),否则无法使用索引扫描排序[^3]。 - WHERE条件中的列可以使用索引前缀:例如,索引为idx_users_age_name,WHERE age = 25 ORDER BY name 可以触发索引扫描,因为WHERE使用索引前缀[^3][^4]。 #### 3. **优化技巧和注意事项** - **使用覆盖索引**:创建索引时包含SELECT所需的所有列(即覆盖索引),这样数据库仅扫描索引而不访问数据表,进一步提升性能(Extra列显示"Using index")[^4][^5]。 - **索引跳跃扫描**:对于复合索引缺失前缀的场景(如索引idx_users_age_name,但ORDER BY name),MySQL可能使用索引跳跃扫描进行优化。但这不是总是发生,因为数据库会根据成本(如I/O和CPU开销)选择最低成本方案[^4]。 - **成本权衡**:索引扫描排序虽然高效,但创建过多索引会增加写入开销。应结合查询模式设计索引,例如优先为高频排序字段创建索引。 - **数据库兼容性**:大多数支持索引的数据库(如MySQLSQLite)都支持此机制。示例SQLite代码: ```sql -- 创建表和索引 CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT, age INT); CREATE INDEX idx_users_age ON users(age); -- 查询触发索引扫描排序 EXPLAIN QUERY PLAN SELECT * FROM users ORDER BY age; -- 输出中若显示"SCAN TABLE users USING INDEX idx_users_age",表示使用索引扫描排序[^2] ``` #### 4. **验证与调试** - 使用EXPLAIN分析查询:在MySQL中运行EXPLAIN SELECT ...,检查type列是否为"index",以及Extra列是否有"Using filesort"(表示未使用索引扫描)[^1][^5]。 - 示例调试: - 成功案例:索引idx_users_age,查询SELECT * FROM users ORDER BY age,EXPLAIN输出type="index"。 - 失败案例:索引idx_users_age,但查询ORDER BY age, name(未创建对应复合索引),会显示type="ALL"或Extra="Using filesort"。 通过合理设计索引并满足上述条件,您可以充分利用索引扫描实现高效排序。注意,实际应用中应测试不同场景的性能,因为索引维护成本可能影响整体效率[^4]。如果您有具体查询示例,我可以帮助分析优化方案。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值