Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 585|回复: 0

开发交流:JAVA(SQL语句的优化)

[复制链接]

该用户从未签到

发表于 2011-10-24 10:01:27 | 显示全部楼层 |阅读模式
一、SQL 编写注意事项(标准)
在多在应用系统开发初期,由于开发数据库数据比较少,对于查询SQL语句的编写等体会不出SQL语句各种写法的性能优劣,但是如果将应用系统提交实际应用后,随着数据库中数据的增加,系统的响应速度就成为目前系统需要解决的最主要的问题之一。系统优化中一个很重要的方面就是SQL语句的优化。对于大量数据,劣质SQL语句和优质SQL语句之间的速度差别可以达到上百倍,可见对
于一个系统不是简单地能实现其功能就可,而是要写出高质量的SQL语句,提高系统的可用性。
在多数情况下,Oracle使用索引来更快地遍历表,优化器主要根据定义的索引来提高性能。但是,如果在SQL语句的where子句中写的SQL代码不合理,就会造成优化器删去索引而使用全表扫描,一
般就这种SQL语句就是所谓的劣质SQL语句。在编写SQL语句时我们应清楚优化器根据何种原则来使
用索引,这有助于写出高性能的SQL语句。
二、SQL语句的编写原则:
●选择一个好的表联接顺序(这是一个比较重要的原则)
当在WHERE子句中有多个表联接时,WHERE子句中排在最后的表应当是返回行数可能最少的表,有过滤条件的子句应放在WHERE子句中的最后。
如:设从emp表查到的数据比较少或该表的过滤条件比较确定,能大大缩小查询范围,则将最具有选择性部分放在WHERE子句中的最后:
select * from emp e,dept d where d.deptno >10 and e.deptno =30; 如果dept表返回的记录数较多的话,上面的查询语句会比下面的查询语句响应快得多。
select * from emp e,dept d where e.deptno =30 and d.deptno >10;
●最好不要在WHERE子句中使用函数或表达式,如果要使用的话,最好统一使用相同的表达式或函数,这样便于以后使用合理的索引。
SELECT *  FROM T1 WHERE  F1*2 = 100
改成
SELECT *  FROM T1 WHERE  F1 = 100/2
● 使用WHERE (NOT)EXISTS 来代替(NOT)IN子句,使用NOT EXISTS 子句可以有效地利用索引。
尽可能使用NOT EXISTS来代替NOT IN,尽管二者都使用了NOT(但NOT IN不能使用索引而降低速度),NOT EXISTS要比NOT IN查询效率更高。
例子1:  
SELECT dname, deptno FROM dept WHERE deptno NOT IN (SELECT deptno FROM emp);
例子2:
SELECT dname, deptno FROM dept WHERE NOT EXISTS (SELECT deptno FROM emp WHERE dept.deptno = emp.deptno);
明显的,2要比1的执行性能好很多。
因为1中对emp进行了全表扫描,这是很浪费时间的操作。而且1中没有用到emp的索引, 因为没有where子句。而2中的语句对emp进行的是缩小范围的查询。
● 通过使用>=、<=等,避免使用NOT命令
如这个例子:
select * from employee where salary<>3000;
对这个查询,可以改写为不使用NOT:
select * from employee where salary<3000 or salary>3000;
虽然这两种查询的结果一样,但是第二种查询方案会比第一种查询方案更快些。第二种查询允许Oracle对salary列使用索引,而第一种查询则不能使用索引。
● 外部联接 + 的用法
外部联接+按其在=的左边或右边分左联接和右联接。若不带+运算符的表中的一个行不直接匹配于带+预算符的表中的任何行,则前者的行与后者中的一个空行相匹配并被返回。利用外部联接+,可以替代效率十分低下的 not in 运算,大大提高运行速度。例如,下面这条命令执行起来很慢:
select a.empno from emp a where a.empno not in
(select empno from emp1 where job=‘SALE’);
索引倘若利用外部联接,改写命令如下:  
select a.empno from emp a left join emp1 b
on a.empno=b.empno
where b.empno is null
and b.job=‘SALE’;
例如表少,但情况复杂的时候应该写如下语句:
select a.empno AS empno,
(select emp2.address from emp2 where a.id=emp2.id) AS address,
(select emp3.address1 from emp3 where b.id=emp3.id) AS address1
from emp a left join emp1 b on a.empno=b.empno
where b.empno is null and b.job=‘SALE’
这样运行速度明显提高.
●在查询时尽量少用格式转换
如用 WHERE a.order_no = b.order_no
而不用
WHERE TO_NUMBER (substr(a.order_no, instr(b.order_no, ’.’)-1)
= TO_NUMBER (substr(a.order_no, instr(b.order_no, .’) - 1)
●Order by语句
索引ORDER BY语句决定了Oracle如何将返回的查询结果排序。Order by语句对要排序的列没有什么特别的限制,也可以将函数加入列中(象联接或者附加等)。任何在Order by语句的非索引项或者有计算表达式都将降低查询速度。
仔细检查order by语句以找出非索引项或者表达式,它们会降低性能。解决这个问题的办法就是重写order by语句以使用索引,也可以为所使用的列建立另外一个索引,同时应绝对避免在order by子句中使用表达式。
如必须使用排序操作,请遵循如下规则: 如结果集不需唯一,使用union all代替union。
●IS NULL 与 IS NOT NULL
不能用null作索引,任何包含null值的列都将不会被包含在索引中。即使索引有多列这样的情况下,只要这些列中有一列含有null,该列就会从索引中排除。也就是说如果某列存在空值,即使对该列建索引也不会提高性能。
任何在where子句中使用is null或is not null的语句优化器是不允许使用索引的。
       ●SELECT子句中避免使用 ‘ *‘
            SELECT * FROM EMP
            应改为:
            SELECT COLUMN FROM EMP
       ●当在SQL语句中连接多个表时, 最好使用表别名并把别名加在每个列上
       ●其他
使用count(*)而不要使用count(column_name)。
避免困难的正规表达式:LIKE通配符匹配,技术上叫正规表达式。但这种匹配特别耗费时间。例如:SELECT * FROM customer WHERE zipcode LIKE “98_ _ _”
即使在zipcode字段上建立了索引,也还是采用顺序扫描的方式。如果把语句改为
SELECT * FROM customer WHERE zipcode >98000
在执行查询时就会利用索引来查询,显然会大大提高速度。
另外,以下语句也不会使用索引:
SELECT * FROM customer WHERE zipcode LIKE '[C-P]arsen'
SELECT * FROM customer WHERE zipcode LIKE 'parsen [^L]%'
SELECT * FROM customer WHERE zipcode LIKE '%parsen'
         
三、其他经验性规则
  1、用多表连接代替EXISTS子句。如例(1)
   2、少用DISTINCT,用EXISTS代替 如例(2),where 条件满足后,可以一次 
性返回结果集
3、如果有两张表联接利用COUNT的时候最好将急纪录少的表放放在后后面
 表 TAB1 16,384 条记录
     表 TAB2 5000 条记录
select count(tab2 .*) from tab1,tab2 where tab2.id=tab1.id这个
速度要快些。
select count(tab2 .*) from tab2,tab1 where tab2.id=tab1.id
如果还有tab3表,而且tab3表是tab1和tab2表的联协表,也叫交集表,
此时,将tab3放在最后效率要高很多。
select count(tab2 .*) from tab2,tab1,tab3 where tab2. name=tab1. name and tab3.sex=tab1.sex and tab3.age=tab2.age
示例:

例1:
   X   SELECT * FROM emp WHERE EXISTS ( SELECT 'X' FROM dept
    WHERE dept_no=e.dept_no AND dept_cat='A');
   O   SELECT * FROM emp e,dept d WHERE e.dept_no=d.dept_no
    AND dept_cat='A';
例2:
   X   SELECT DISTINCT d.dept_code,d.dept_name FROM dept d ,emp e
    WHERE e.dept_code=d.dept_code;
   O   SELECT dept_code,dept_name FROM dept d
    WHERE EXISTS ( SELECT 'X' FROM emp e
    WHERE e.dept_code=d.dept_code);
SQL 性能调整原则
以下就某些SQL语句的where子句编写及性能优化中需要注意的问题作详细介绍。
在这些where子句中,虽然某些列存在索引,但是由于编写了劣质的SQL,系统在运行该SQL语句时也不能使用该索引,而同样使用全表扫描,这就造成了响应速度的极大降低。
1. IS NULL 与 IS NOT NULL
  在应不能用null作索引,任何包含null值的列都将不会被包含在索引中。即使索引有多列这样的情况下,只要这些列中有一列含有null,该列就会从索引中排除。也就是说如果某列存在空值,即使对该列建索引也不会提高性能。任何在where子句中使用is null或is not null的语句优化器是不允许使用索引的。
例:
drop index temp_zhao.aaaindex
select *  from temp_zhao where  aulname is not null
create clustered index aaaindex on temp_zhao(aulname)
select *  from temp_zhao where aulname is not null
2. 联接列
在应对于有联接的列,即使最后的联接值为一个静态值,优化器是不会使用索引的。
我们一起来看一个例子,假定有一个职工表(employee),对于一个职工的姓和名分成两列存放(FIRST_NAME和LAST_NAME),现在要查询一个叫比尔.克林顿(Bill Cliton)的职工。
下面是一个采用联接查询的SQL语句:
   select * from employss
   where
   first_name||''||last_name ='Beill Cliton';
上面这条语句完全可以查询出是否有Bill Cliton这个员工,但是这里需要注意,系统优化器对基于last_name创建的索引没有使用。应当采用下面这种SQL语句的编写,Oracle系统就可以采用基于last_name创建的索引。
   Select * from employee
   where
   first_name ='Beill' and last_name ='Cliton';
         例:
drop index temp_zhao.aaaindex
select * from temp_zhao where aulname+' '+au_fname = 'White Johnson'
select * from temp_zhao where aulname = 'White' and  au_fname='Johnson'
create clustered index aaaindex on temp_zhao(au_fname)
select * from temp_zhao where aulname+' '+au_fname = 'White Johnson'
select * from temp_zhao where aulname = 'White' and  au_fname='Johnson'
3. 带通配符(%)的like语句
同样以上面的例子来看这种情况。目前的需求是这样的,要求在职工表中查询名字中包含
cliton的人。可以采用如下的查询SQL语句:
select * from employee where last_name like '%cliton%';
这里由于通配符(%)在搜寻词首出现,所以Oracle系统不使用last_name的索引。在很多情况下可能无法避免这种情况,但是一定要心中有底,通配符如此使用会降低查询速度。然而当通配符出现在字符串其他位置时,优化器就能利用索引。在下面的查询中索引得到了使用:
    select * from employee where last_name like 'cliton%';
例:
drop index temp_zhao.aaaindex
select * from temp_zhao where aulname like 'G%' or aulname like 'W%'or aulname like 'b%'
create clustered index aaaindex on temp_zhao(aulname)
select *  from temp_zhao where  aulname like 'G%' or aulname like 'W%' or aulname like 'b%'
4. Order by语句
ORDER BY语句决定了Oracle如何将返回的查询结果排序。Order by语句对要排序的列
没有什么特别的限制,也可以将函数加入列中(象联接或者附加等)。任何在Order by语句的非索
引项或者有计算表达式都将降低查询速度。仔细检查order by语句以找出非索引项或者表达式,
它们会降低性能。解决这个问题的办法就是重写order by语句以使用索引,也可以为所使用的列
建立另外一个索引,同时应绝对避免在order by子句中使用表达式。
例:
    Order by 语句
drop index temp_zhao.aaaindex
select  * from temp_zhao order by  au_id
create clustered index aaaindex on temp_zhao(au_id)
select  * from temp_zhao order by  au_id
Group by 语句
drop index temp_zhao.aaaindex
select  aulname from temp_zhao group by  aulname
create clustered index aaaindex on temp_zhao(aulname)
select  aulname from temp_zhao group by  aulname
5. NOT
我们在查询时经常在where子句使用一些逻辑表达式,如大于、小于、等于以及不等于等等,
也可以使用and(与)、or(或)以及not(非)。NOT可用来对任何逻辑运算符号取反。下面是一
个NOT子句的例子:
... where not (status ='VALID')
如果要使用NOT,则应在取反的短语前面加上括号,并在短语前面加上NOT运算符。NOT运算
符包含在另外一个逻辑运算符中,这就是不等于(<>)运算符。换句话说,即使不在查询where
子句中显式地加入NOT词,NOT仍在运算符中,见下例:
... where status <> 'INVALID';
再看下面这个例子:
      select * from employee where  salary<>3000;
对这个查询,可以改写为不使用NOT:
      select * from employee where  salary<3000 or salary>3000;
例:
drop index temp_zhao.aaaindex
select *  from temp_zhao where  zip<>94609
create clustered index aaaindex on temp_zhao(zip)
select *  from temp_zhao where  zip<>94609
select *  from temp_zhao where  zip>94609 or zip<94609
虽然这两种查询的结果一样,但是第二种查询方案会比第一种查询方案更快些。第二种查询允许Oracle对salary列使用索引,而第一种查询则不能使用索引。
6. IN和EXISTS
有时候会将一列和一系列值相比较。最简单的办法就是在where子句中使用子查询。在where
子句中可以使用两种格式的子查询。
第一种格式是使用IN操作符:
    ... where column in(select * from ... where ...);
第二种格式是使用EXIST操作符:
    ... where exists (select 'X' from ...where ...);
    相信绝大多数人会使用第一种格式,因为它比较容易编写,而实际上第二种格式要远比第一种格式的效率高。在Oracle中可以几乎将所有的IN操作符子查询改写为使用EXISTS的子查询。
在第二种格式中,子查询以‘select 'X'开始。运用EXISTS子句不管子查询从表中抽取什么数据它只查看where子句。这样优化器就不必遍历整个表而仅根据索引就可完成工作(这里假定在where语句中使用的列存在索引)。相对于IN子句来说,EXISTS使用相连子查询,构造起来要比IN子查询困难一些。通过使用EXIST,Oracle系统会首先检查主查询,然后运行子查询直到它找到第一个匹配项,这就节省了时间。Oracle系统在执行IN子查询时,首先执行子查询,并将获得的结果列表存放在在一个加了索引的临时表中。在执行子查询之前,系统先将主查询挂起,待子查询执行完毕,存放在临时表中以后再执行主查询。这也就是使用EXISTS比使用IN通常查询速度快的原因。同时应尽可能使用NOT EXISTS来代替NOT IN,尽管二者都使用了NOT(不能使用索引而降低速度),NOT EXISTS要比NOT IN查询效率更高。
例:
create clustered index aaaindex on temp_zhao(au_id)
select * from temp_zhao where temp_zhao.au_id  in (select au_id from temp_zhao1)
select * from temp_zhao where exists (select * from temp_zhao1 where temp_zhao.au_id=temp_zhao1.au_id)
7、其他
1、SELECT子句中避免使用 ‘ *‘
  select zip from temp_zhao
  select * from temp_zhao
2、使用count(*)而不要使用count(column_name)。
select count(zip) from temp_zhao
select count(*) from temp_zhao
  
培训注意点总结
1、语句中出现的所有表名、字段名全部小写,系统保留字、内置函数名、SQL保留字大写。
2、连接符or、in、and、以及=、<=、>=等前后加上一个空格。
3、对较为复杂的sql语句加上注释,说明算法、功能。
4、查询多个列时,尽量使每列查询占一行,且加上注释来标明该列名称,以方便其他人以后的修改。
5、where子句书写时,每个条件占一行,语句令起一行时,以保留字或者连接符开始,连接符右对齐。
6、多表连接时,使用表的别名来引用列。
7、变量命名不能超出ORACLE的限制(30个字符),命名要规范,要用英文命名。
8、查找数据库表或视图时,只取需要字段,不要使用*来代替所有列名。
9、当在WHERE子句中有多个表联接时,WHERE子句中排在最后的表应当是返回行数可能最少的表。
10、过滤条件最有效的子句应放在WHERE子句中的最后。
11、最好不要在WHERE子句中使用函数或表达式。
12、尽可能使用NOT EXISTS来代替NOT IN,使用EXISTS来代替IN。
13、通过使用>... OR <...来避免使用<>...。
14、在查询时尽量少用格式转换。
15、在不必要的情况下尽量避免使用ORDER BY和GROUP BY(必要时注意对索引列排序、分组)。
16、尽量避免使用IS NULL 和 IS NOT NULL(尤其对具有索引的列使用)。
17、在使用count时,尽量使用count(*)而不要使用count(column_name)。
18、在不必要的情况下尽量避免使用DISTINCT。
19、尽量避免使用‘%...‘。
20、尽可能多地使用主键列(默认的会在主键上加上索引)作为条件。
21、尽量避免使用UNION ALL,而用UNION代替。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

GMT+8, 2025-1-11 01:59 , Processed in 0.300742 second(s), 36 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表