目录
1、
书写格式规范
1-1、大小文字及空格的统一
1-2、日期格式明确化
1-3、Bind变量的使用
1-4、表别名的使用
1-5、检索时尽量避免检索不需要的列
1-6、ORDER BY列明确指定
1-7、插入列明确指定
1-8、关联表数目的限制
1-9、子查询中不使用视图
1-10、Hint的写法
1-11、命名规范
2、
索引应用规范
2-1、WHERE子句中尽量不用OR
2-2、WHERE子句中尽量不用LIKE ‘%c%’,LIKE ’%c’
2-3、WHERE子句中尽量不用NOT
2-4、WHERE子句中尽量不用IS NULL,IS NOT NULL
2-5、在WHERE中慎用<>,!=
2-6、WHERE子句中IS NOT NULL使用的补充
2-7、尽量用EXISTS代替DISTINCT
2-8、ORDER BY使用上的注意点
2-9、Index列尽量不参加计算
2-10、复合Index中前面的列应在条件中指定
3、
其他规范
3-1、列比较时尽量保持类型一致
3-2、尽量避免使用子查询
3-3、对子查询层次限定
3-4、尽量用NOT EXISTS代替NOT IN + 子查询
3-5、用表连接替换EXISTS
3-5、尽量不使用HAVING子句
3-6、为FROM子句中的表指定顺序
3-7、WHERE子句中的连接顺序
3-8、ROWID的使用
3-9、用ROWNUM判断纪录是否存在
3-10、翻页sql(下N件记录取得)的写法
3-11、MERGE的使用
3-12、多表插入的使用
3-13、DBlink使用的限制
3-14、尽量用DECODE代替集合函数
3-15、删除重复记录
3-16、减量减少对表的查询
3-17、避免使用耗费资源的操作
1、 书写格式规范
1-1、大小文字及空格的统一
数据库对象名(表、索引等)和变量全半角小写,其他的sql文全半角大写,避免不要的空格,如果使用空格只使用半角空格,既保持sql文的可读性,又尽可能的减少sql的解析时间。
因为ORACLE的共享sql机制,只有在共享池中找到完全匹配的(字符级比较,包括空格,字符大小写)sql才不需要再次解析(parsing)被执行sql,所以应尽量保持统一风格的sql。
错:select E.EMP_NO FROM emp E;
对:SELECT e.emp_no FROM emp e;
1-2、日期格式明确化
如果不明确的指定时间的格式,默认使用NLS_DATE_FORMAT参数指定的时间格式,就有可能得不到你想要的结果,所以需要用TO_CHAR或TO_DATE函数明确转换再使用。
错:SELECT e.ename,e.hire_date FROM emp e WHERE e.hire_date > ‘20100726’
对:SELECT e.ename,TO_CHAR(e.hire_date,’YYYYMMDD’) FROM emp e WHERE e.hire_date > TO_DATE(‘20100726’,’YYYYMMDD’)
1-3、Bind变量的使用
对于相同条件不同条件值的sql文,采用bind变量使其变成同一sql文,从而减少ORACLE对它的解析时间。(Java语言中一般用?来作为bind变量)
错:SELECT e.ename FROM emp e WHERE e.emp_no = 123;
SELECT e.ename FROM emp e WHERE e.emp_no = 567;
对:SELECT e.ename FROM emp e WHERE e.emp_no = ?;
1-4、表别名的使用
在进行多表关联查询时,检索列前加上表别名,这样既明确列的定义,又减少了sql文的解析时间。
错:SELECT ename,dname FROM emp,dept WHERE emp.deptno=dept.deptno AND sal >1000;
对:SELECT e.ename,d.dname FROM emp e,dept d WHERE e.deptno=d.deptno AND e.sal >1000;
1-5、检索时尽量避免检索不需要的列
在检索纪录时,特别是纪录的列比较多时,尽量不要使用‘*’来代替所有列,这样不仅增加处理时间(ORACLE在解析的过程中,会将‘*’ 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间,同时也增加了I/O的量),而且当表结构变化时,原来的列顺序有可能完全改变而导致不必要的bug或修改。
错:SELECT * FROM emp WHERE eadrs = “上海市”;
对:SELECT e.empno e.ename FROM emp WHERE e.eadrs = “上海市”;
1-6、ORDER BY列明确指定
虽然使用ORDER BY字句时可以根据SELECT句中检索列的列号来指定所需排序的列,但是从代码的维护性考虑,应该尽量不使用列号而是明确指定排序列来排序。
错:SELECT e.emp_no,e.ename,e.sal FROM emp e ORDER BY 2;
对:SELECT e.emp_no,e.sal FROM emp e ORDER BY e.ename;
1-7、插入列明确指定
在对对象表进行全列插入时,sql文法上允许省略对列的指定,但是考虑到因表结构(列的增删改)有可能发生变化,而减少不要的修改,即使全列插入也需要明确指定插入列。
错:INSERT INTO emp VALUES(100,‘AAA’);
对:INSERT INTO emp(emp_no,ename) VALUES(100,‘AAA’);
1-8、关联表数目的限制
在进行多表关联查询时,随着关联表的增多ORACLE访问路径也会变得越来越复杂,从而导致ORACLE执行了不合理的实行计划,所以要避免不必要的关联。
错:SELECT ~ FROM emp1 e1,emp2 e2,emp3 e3; //emp3为不需要关联的表
对:SELECT ~ FROM emp1 e1,emp2 e2;
1-9、子查询中不使用视图
在子查询中使用视图会使ORACLE的访问路径变得很复杂,从而导致ORACLE执行了不合理的实行计划,所以尽量不要在子查询中使用视图。
CREATE VIEW emp_v AS SELECT e.name,e.sal*12 sal FROM emp e WHERE e.deptno < 120;
错:SELECT t1.dept_no,t2.ename FROM dept1 t1,(SELECT v.ename,FROM emp_v WHERE v.sal < 3000) t2 WHERE t1.dept_no = t2.dept_no;
对:SELECT t1.dept_no,FROM emp e WHERE e.salary < (3000/12) AND e.dept_no < 120) t2 WHERE t1.dept_no = t2.dept_no;
1-10、Hint的写法
一般的sql中不考虑使用hint句,除非在性能试验等试验结果性能很差的时候才考虑使用。(使用hint之后会改变原有的实行计划)
错:SELECT /*+FIRST_ROWS*/ e.ename FROM emp e WHERE e.emp_no = 'SCOTT';
对:SELECT e.ename FROM emp e WHERE e.emp_no = 'SCOTT';
1-11、命名规范
表别名,列别名命名时,尽量按照原表名和列名的省略缩写形式,保持sql的可读性。
错:[表名]employee -->[别名]a
对:[表名]employee -->[别名]emp
1-1、大小文字及空格的统一
数据库对象名(表、索引等)和变量全半角小写,其他的sql文全半角大写,避免不要的空格,如果使用空格只使用半角空格,既保持sql文的可读性,又尽可能的减少sql的解析时间。
因为ORACLE的共享sql机制,只有在共享池中找到完全匹配的(字符级比较,包括空格,字符大小写)sql才不需要再次解析(parsing)被执行sql,所以应尽量保持统一风格的sql。
错:select E.EMP_NO FROM emp E;
对:SELECT e.emp_no FROM emp e;
1-2、日期格式明确化
如果不明确的指定时间的格式,默认使用NLS_DATE_FORMAT参数指定的时间格式,就有可能得不到你想要的结果,所以需要用TO_CHAR或TO_DATE函数明确转换再使用。
错:SELECT e.ename,e.hire_date FROM emp e WHERE e.hire_date > ‘20100726’
对:SELECT e.ename,TO_CHAR(e.hire_date,’YYYYMMDD’) FROM emp e WHERE e.hire_date > TO_DATE(‘20100726’,’YYYYMMDD’)
1-3、Bind变量的使用
对于相同条件不同条件值的sql文,采用bind变量使其变成同一sql文,从而减少ORACLE对它的解析时间。(Java语言中一般用?来作为bind变量)
错:SELECT e.ename FROM emp e WHERE e.emp_no = 123;
SELECT e.ename FROM emp e WHERE e.emp_no = 567;
对:SELECT e.ename FROM emp e WHERE e.emp_no = ?;
1-4、表别名的使用
在进行多表关联查询时,检索列前加上表别名,这样既明确列的定义,又减少了sql文的解析时间。
错:SELECT ename,dname FROM emp,dept WHERE emp.deptno=dept.deptno AND sal >1000;
对:SELECT e.ename,d.dname FROM emp e,dept d WHERE e.deptno=d.deptno AND e.sal >1000;
1-5、检索时尽量避免检索不需要的列
在检索纪录时,特别是纪录的列比较多时,尽量不要使用‘*’来代替所有列,这样不仅增加处理时间(ORACLE在解析的过程中,会将‘*’ 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间,同时也增加了I/O的量),而且当表结构变化时,原来的列顺序有可能完全改变而导致不必要的bug或修改。
错:SELECT * FROM emp WHERE eadrs = “上海市”;
对:SELECT e.empno e.ename FROM emp WHERE e.eadrs = “上海市”;
1-6、ORDER BY列明确指定
虽然使用ORDER BY字句时可以根据SELECT句中检索列的列号来指定所需排序的列,但是从代码的维护性考虑,应该尽量不使用列号而是明确指定排序列来排序。
错:SELECT e.emp_no,e.ename,e.sal FROM emp e ORDER BY 2;
对:SELECT e.emp_no,e.sal FROM emp e ORDER BY e.ename;
1-7、插入列明确指定
在对对象表进行全列插入时,sql文法上允许省略对列的指定,但是考虑到因表结构(列的增删改)有可能发生变化,而减少不要的修改,即使全列插入也需要明确指定插入列。
错:INSERT INTO emp VALUES(100,‘AAA’);
对:INSERT INTO emp(emp_no,ename) VALUES(100,‘AAA’);
1-8、关联表数目的限制
在进行多表关联查询时,随着关联表的增多ORACLE访问路径也会变得越来越复杂,从而导致ORACLE执行了不合理的实行计划,所以要避免不必要的关联。
错:SELECT ~ FROM emp1 e1,emp2 e2,emp3 e3; //emp3为不需要关联的表
对:SELECT ~ FROM emp1 e1,emp2 e2;
1-9、子查询中不使用视图
在子查询中使用视图会使ORACLE的访问路径变得很复杂,从而导致ORACLE执行了不合理的实行计划,所以尽量不要在子查询中使用视图。
CREATE VIEW emp_v AS SELECT e.name,e.sal*12 sal FROM emp e WHERE e.deptno < 120;
错:SELECT t1.dept_no,t2.ename FROM dept1 t1,(SELECT v.ename,FROM emp_v WHERE v.sal < 3000) t2 WHERE t1.dept_no = t2.dept_no;
对:SELECT t1.dept_no,FROM emp e WHERE e.salary < (3000/12) AND e.dept_no < 120) t2 WHERE t1.dept_no = t2.dept_no;
1-10、Hint的写法
一般的sql中不考虑使用hint句,除非在性能试验等试验结果性能很差的时候才考虑使用。(使用hint之后会改变原有的实行计划)
错:SELECT /*+FIRST_ROWS*/ e.ename FROM emp e WHERE e.emp_no = 'SCOTT';
对:SELECT e.ename FROM emp e WHERE e.emp_no = 'SCOTT';
1-11、命名规范
表别名,列别名命名时,尽量按照原表名和列名的省略缩写形式,保持sql的可读性。
错:[表名]employee -->[别名]a
对:[表名]employee -->[别名]emp
2-1、WHERE子句中尽量不用OR
使用了OR之后,有的情况下(涉及到索引的扫描方式)会使对索引的检索变成无效,从而降低检索的性能,这这种情况下,可以考虑是否可以用UNION或IN来代替。
RBO(基于规则)下,OR就像UNION ALL文的展开一样,在有外部结合、CONNECT BY句的情况下就不能被执行。CBO(基于成本)下,OR根据各结合顺序的基础结合成本,在再估算阶段,根据成本对INLIST和OR文能不能再扩展进行判断。
错1:SELECT name FROM emp WHERE deptno = 99 OR deptno = 999;
对1:SELECT name FROM emp WHERE deptno = 99
UNION ALL
SELECT name FROM emp WHERE deptno = 999;
错2:~ FROM emp WHERE deptno = 10 OR deptno = 20 OR teptno = 30;
对2:~ FROM emp WHERE deptno IN(10,20,30);
2-2、WHERE子句中尽量不用LIKE ‘%c%’,LIKE ’%c’
在有索引的列上使用LIKE ‘%c%’,LIKE ’%c’之后,索引就会失效,因而性能也会下降。
错:~ WHERE name LIKE ‘%c%’;
错:~ WHERE name LIKE ’%c’;
对:~ WHERE name LIKE ’ c%’;
2-3、WHERE子句中尽量不用NOT
在有索引的列上使用NOT、!=、 <>之后,索引检索就会失效,因而性能也会下降。
例1:job列大部分值为NULL或’SALESMAN’,并且job列上建有B*Tree索引。
错:~ WHERE job != ‘SALESMAN’
对:~ WHERE job > ‘SALESMAN’ OR job < ‘SALESMAN’
例2:dname为建有B*Tree索引的文字列,并且基数比较少(值比较少,譬如性别列,只有男和女两个值),且不经常被更新。
错:~ WHERE dname IS NOT NULL;
对:DROP INDEX btree_dname_idx;
CREATE BITMAP INDEX bitmap_dname_idx ON emp(dname);
SELECT ~ WHERE dname IS NOT NULL;
2-4、WHERE子句中尽量不用IS NULL,IS NOT NULL
在用了IS NULL、IS NOT NULL 之后,对应检索列的B*Tree索引就会失去索引功能,从而性能会大幅下降。
在使用IS NULL的时候也应该考虑一下是否真的需要NULL值。IS NOT NULL在CBO下统计情报取得的情况下,索引的检索也有可能被使用(参照IS NOT NULL使用的补充)。即使使用IS NULL、IS NOT NULL,BITMAP索引还是照样能被使用。
错:Hiredate列为日期型,在hiredate列上建有B*Tree索引,并且’9999-12-31’是系统中不可能出现的日期,执行下面的sql
SELECT ~ WHERE hiredate IS NULL;
对:CREATE INDEX function_hiredate_idx ON emp(NVL(hiredate,TO_DATE(‘9999-12-31’)));
SELECT ~ WHERE NVL(hiredate,TO_DATE(‘9999-12-31’)) = TO_DATE(‘9999-12-31’,‘YYYYMMDD’);
2-5、在WHERE中慎用<>,!=
记住索引只能告诉你什么存在于表中,而不能告诉你什么不存在于表中。
在下面的例子中‘!=' 将不使用索引。
不使用索引:SELECT account_name FROM transaction WHERE amout != 0;
使用索引:SELECT account_name FROM transaction WHERE amout > 0;
2-6、WHERE子句中IS NOT NULL使用的补充
在NULL值比率较低的列上使用IS NOT NULL,会使对B*Tree索引变得无效,从而降低检索性能,相反,在NULL值比率相当高的列上使用IS NOT NULL,B*Tree索引会使用全表扫描(full scan)从而地高性能。(这是在CBO中统计情报已经取得的条件下)
例:Comm为建有B*Tree索引的数值型列,且NULL值比率相当高(CBO并且统计情报已经取得)
错:~ WHERE comm >= 0 OR comm < 0;
对:~ WHERE comm IS NOT NULL;
2-7、尽量用EXISTS代替DISTINCT
DEPT表和EMP表是一对多的关系,根据EMP表从DEPT表中取出纪录,这种情况要避免使用DISTINCT,因为用DISTINCT后满足条件的所有纪录将都会被检索、排序、重复行删除,从而影响了性能。取而代之的可以用EXSITS子查询,EXSITS子查询当有一件符合条件的纪录存在时马上就返回不处理剩下的纪录,因而速度很快。
错:SELECT DISTINCT d.dept_code,d.dept_name FROM dept d,emp e WHERE e.dept_code = d.dept_code;
对:SELECT d.dept_code,d.dept_name FROM dept d WHERE EXSITS(SELECT ‘X’ FROM emp e WHERE e.dept_code = d.dept_code);
2-8、ORDER BY使用上的注意点
如果想要ORDER BY句的排序用到索引,就必须满足下面的两个条件
1)、ORDER BY句中的列顺序要完全包含在同一顺序的一个复合索引中
2)、ORDER BY句中的列要全部在表中定义为NOT NULL列
2-9、Index列尽量不参加计算
检索条件中索引列被参与计算,或被用作函数的参数,那么就会失去该列的索引功能,从而导致性能急剧下降。
可以通过建函数索引的方法,计算结果或函数值事前计算好作为所引来用。
错:~ WHERE sal*1.1 > 950;
对:~ WHERE sal > 950/1.1;
错:~ WHERE name || type =’XXXY’ ;
对:~ WHERE name = ‘XXX’ AND type = ’Y’ ;
错:~ WHERE TO_CHAR(hiredate,‘YYYYMMDD’) = ‘20100722’;
对:~ WHERE hiredate = TO_DATE( ‘20100722’,‘YYYYMMDD’) ;
错:~ WHERE SUBSTR(name,1,7) = ‘CAPTIAL’;
对:~ WHERE name LIKE ‘CAPTIAL%’;
错:~ WHERE TRUNC(trans_date) = TRUNC(SYSDATE);
对:~ WHERE trans_date BETWEEN TRUNC(SYSDATE) AND TURNC(SYSDATE) + .99999
注:在日期上加上超过5位小数的数是,日期就自动变为下一日的日期。
TO_DATE(‘2010-7-22’)+.99999  ‘2010-7-22 23:59:59’
TO_DATE(‘2010-7-22’)+.999999  ‘2010-7-23 00:00:00’
2-10、复合Index中前面的列应在条件中指定
在使用复合索引时,复合索引中前面的列要在条件中指定。如果前面列在条件中没指定的情况下,虽然通过索引的SKIP SCAN机能有可能会使用索引,但是一定要确认索引是否真的被使用。
(depno,job)列顺序的复合索引
对:~ WHERE depno = 20 AN job = ‘MANAGER’;
对:~ WHERE job = ‘MANAGER’ AND depno = 20;
对:~ WHERE depno = 20; //索引前方部分使用
对:~ WHERE job = ‘MANAGER’; //这种情况下,一定要确认索引是否被使用