人们在使用SQL时往往会陷入一个误区,即太关注于所得的结果是否正确,而忽略了不同的实现方法之间可能存在的性能差异,这种性能差异在大型的或是复杂的数据库环境中(如联机事务处理OLTP或决策支持系统DSS)中表现得尤为明显。
笔者在工作
实
践中
发现
,不良的
SQL
往往来自于不恰当的索引
设计
、不充份的
连
接条件和不可
优
化的
where
子句。
在
对
它
们进
行适当的
优
化后,其运行速度有了明
显
地提高!
下面我将从
这
三个方面分
别进
行
总结
:
一、不合理的索引设计----
例:表
record
有
620000
行,
试
看在不同的索引下,下面几个
SQL
的运行情况:
---- 1.
在
date
上建有一非个群集索引
select count(*) from record where date >'19991201' and date < '19991214'and amount >2000 (25秒)
select date ,sum(amount) from record group by date(55秒)
select count(*) from record where date >'19990901' and place in ('BJ','SH') (27秒)
----
分析:
----
date
上有大量的重
复值
,在非群集索引下,数据在物理上随机存放在数据
页
上,在范
围查
找
时
,必
须执
行一次表
扫
描才能找到
这
一范
围
内的全部行。
---- 2.
在
date
上的一个群集索引
select count(*) from record where date >'19991201' and date < '19991214' and amount >2000 (14秒)
select date,sum(amount) from record group by date(28秒)
select count(*) from record where date >'19990901' and place in ('BJ','SH')(14秒)
----
分析:
----
在群集索引下,数据在物理上按
顺
序在数据
页
上,重
复值
也排列在一起,因而在范
围查
找
时
,可以先找到
这
个范
围
的起末点,且只在
这
个范
围
内
扫
描数据
页
,避免了大范
围扫
描,提高了
查询
速度。
---- 3.
在
place
,
date
,
amount
上的
组
合索引
select count(*) from record where date >'19991201' and date < '19991214' and amount >2000 (26秒)
select date,sum(amount) from record group by date(27秒)
select count(*) from record where date >'19990901' and place in ('BJ, 'SH')(< 1秒)
----
分析:
----
这
是一个不很合理的
组
合索引,因
为
它的前
导
列是
place
,第一和第二条
SQL
没有引用
place
,因此也没有利用上索引;第三个
SQL
使用了
place
,且引用的所有列都包含在
组
合索引中,形成了索引覆盖,所以它的速度是非常快的。
---- 4.
在
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
中形成了索引覆盖,因而性能达到了最
优
。
---- 5.
总结
:
----
缺省情况下建立的索引是非群集索引,但有
时
它并不是最佳的;合理的索引
设计
要建立在
对
各
种查询
的分析和
预测
上。
一般来
说
:
①
.
有大量重
复值
、且
经
常有范
围查询
(
between, >,<
,
>=,< =
)和
order by
、
group by
发
生的列,可考
虑
建立群集索引;
②
.
经
常同
时
存取多列,且
每
列都含有重
复值
可考
虑
建立
组
合索引;
③
.
组
合索引要尽量使
关键查询
形成索引覆盖,其前
导
列一定是使用最
频
繁的列。
二、不充份的连接条件:
例:表
card
有
7896
行,在
card_no
上有一个非聚集索引,表
account
有
191122
行,在
account_no
上有一个非聚集索引,
试
看在不同的表
连
接条件下,两个
SQL
的
执
行情况:
select sum(a.amount) from account a,card b where a.card_no = b.card_no(20秒)
select sum(a.amount) from account a,card b where a.card_no = b.card_no and a.account_no=b.account_no(< 1秒)
----
分析:
----
在第一个
连
接条件下,最佳
查询
方案是将
account
作外
层
表,
card
作内
层
表,利用
card
上的索引,其
I/O
次数可由以下公式估算
为
:
外
层
表
account
上的
22541
页
+
(外
层
表
account
的
191122
行
*
内
层
表
card
上
对应
外
层
表第一行所要
查
找的
3
页
)
=595907
次
I/O
在第二个
连
接条件下,最佳
查询
方案是将
card
作外
层
表,
account
作内
层
表,利用
account
上的索引,其
I/O
次数可由以下公式估算
为
:外
层
表
card
上的
1944
页
+
(外
层
表
card
的
7896
行
*
内
层
表
account
上
对应
外
层
表
每
一行所要
查
找的
4
页
)
= 33528
次
I/O
可
见
,只有充份的
连
接条件,真正的最佳方案才会被
执
行。
总结:
1.
多表操作在被
实际执
行前,
查询优
化器会根据
连
接条件,列出几
组
可能的
连
接方案并从中找出系
统开销
最小的最佳方案。
连
接条件要充份考
虑带
有索引的表、行数多的表;内外表的
选择
可由公式:外
层
表中的匹配行数
*
内
层
表中
每
一次
查
找的次数确定,乘
积
最小
为
最佳方案。
2.
查
看
执
行方案的方法
--
用
set showplan
on
,打
开
showplan
选项
,就可以看到
连
接
顺
序、使用何
种
索引的信息;想看更
详细
的信息,需用
sa
角色
执
行
dbcc(3604,310,302)
。