如何让你的SQL运行得更快

在使用 SQL 往往会陷入一个 区,即太 注于所得的 果是否正确,而忽略了不同的 实现 方法之 可能存在的性能差异, 这种 性能差异在大型的或是 复杂 的数据 库环 境中(如 机事 务处 OLTP 或决策支持系 DSS )中表 得尤
笔者在工作 践中 发现 ,不良的 SQL 往往来自于不恰当的索引 设计 、不充份的 接条件和不可 化的 where 子句。
们进 行适当的 化后,其运行速度有了明 地提高!
下面我将从 三个方面分 别进 总结
了更直 问题 ,所有 例中的 SQL 运行 时间 经过测试 ,不超 1秒的均表示 < 1 秒)。 ----
测试环 : 主机: HP LH II---- 330MHZ---- 内存: 128 ----
操作系 Operserver5.0.4----
数据 Sybase11.0.3
 
一、不合理的索引 设计 ----
例:表 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 date28秒)
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 date27秒)
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 date11秒)
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_no20秒)
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)
 
三、不可 化的 where 子句
1. 例:下列 SQL 条件 句中的列都建有恰当的索引,但 行速度却非常慢:
select * from record wheresubstring(card_no,1,4)='5378'(13)
select * from record whereamount/30< 100011秒)
select * from record whereconvert(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秒)
你会 发现 SQL 快起来!
2. 例:表 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 上的索引;
实际 上(根据 showplan , 它却采用了 "OR 策略 " ,即先取出 or 子句的行,存入 临时 数据 的工作表中,再建立唯一索引以去掉重 行,最后从 临时 表中 果。因此, 实际过 程没有利用 id_no 上索引,并且完成 时间还 要受 tempdb 数据 性能的影响。
明,表的行数越多,工作表的性能就越差,当 stuff 620000 时间 竟达到 220 秒! 不如将 or 子句分
select count(*) from stuff where id_no='0'select count(*) from stuff where id_no='1'
得到两个 果,再作一次加法合算。因 为每 句都使用了索引, 时间 只有 3 秒,在 620000 行下, 时间 也只有 4 秒。
或者,用更好的方法,写一个 简单 的存 储过 程:
create proc count_stuff asdeclare @a intdeclare @b intdeclare @c intdeclare @d char(10)beginselect @a=count(*) from stuff where id_no='0'select @b=count(*) from stuff where id_no='1'endselect @c=@a+@bselect @d=convert(char(10),@c)print @d
直接算出 果, 时间 同上面一 快!
 
---- 总结 ---- ,所 谓优 化即 where 子句利用了索引,不可 化即 生了表 描或 开销
1. 任何 列的操作都将 致表 描,它包括数据 函数、 算表达式等等, 查询时 要尽可能将操作移至等号右
2.in or 子句常会使用工作表,使索引失效;如果不 生大量重 复值 ,可以考 把子句拆 ;拆 的子句中 应该 包含索引。
3. 要善于使用存 储过 程,它使 SQL 得更加灵活和高效。
从以上 些例子可以看出, SQL 化的 实质 就是在 果正确的前提下,用 化器可以 识别 句,充份利用索引,减少表 描的 I/O 次数,尽量避免表搜索的 生。其 SQL 的性能 化是一个 复杂 程,上述 些只是在 次的一 ,深入研究 及数据 库层 源配置、网 络层 的流量控制以及操作系 统层 设计

 

你可能感兴趣的:(sql)