SQL编码规范建议

更新时间:2023-03-14 00:11:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

SQL编码规范建议

(Oracle)

文档状态 [ √ ] 初稿 [ ] 评审通过 [ ] 修改 [ ] 发布 [ ] 作废 文档标识 当前版本 作 者 部门/厂商 完成日期

SQL编码规范建议(Oracle)

文档版本历史表

版本号 V 0。1 陈强 作者 操作 创建 日期 2012-11-16 说明

文档审核记录表

版本号 审核人 审核人签字 审核日期 说明 ?SSC

- 2 -

SQL编码规范建议(Oracle)

?SSC

- 3 -

SQL编码规范建议(Oracle)

目 录

引言........................................................................................................................................... 5 1.1. 1.2. 1.3. 2.

简介 ................................................................................................................................... 5 目的 ................................................................................................................................... 5 参考文献 ........................................................................................................................... 5

SQL编码规范 .......................................................................................................................... 5 2.1. 2.2. 2.3. 2.4.

查询SQL语句 ................................................................................................................. 5 DML操作 ......................................................................................................................... 7 通用规范 ........................................................................................................................... 8 共享SQL语句 ................................................................................................................. 8

3. 性能优化 ................................................................................................................................... 9 3.1. 3.2. 3.3. 3.4. 3.5. 3.6. 3.7.

执行计划分析SQL性能 ................................................................................................. 9 优化器 ............................................................................................................................. 10 语句顺序(RBO中有效) ............................................................................................ 10 减少数据库或表访问次数 ............................................................................................. 11 函数使用 ......................................................................................................................... 13 索引 ................................................................................................................................. 14 其他 ................................................................................................................................. 15

?SSC

- 4 -

SQL编码规范建议(Oracle)

引言

1.1. 简介

所有的程序开发手册都包含了各种规则。一些习惯自由程序的人(例如 Java 程序员)可能对这些规则很不适应,但是在多个开发人员共同协作的情况下,这些规则是必需的。这不仅仅是为了开发效率,而且也为了测试和后期维护。

良好的编码习惯有助于标准化程序的结构和编码风格,使源代码对于自己和别人都易读和易懂。在开发周期中越早使用恰当的编码规定,将会最大程度的提高项目的生产率。良好的编码习惯除了代码格式,详细的注释外,还应该包括使用有助于提高程序效率的编码方式。

规范的开发有助于提高源码的可读性,可维护性,对于提高项目的整体效率更是不可缺少的(尤其是团队开发)。

1.2. 目的

本文是针对程序员对SQL语句编写过程所应遵循的规范。按照此规范来开发SQL语句可带来以下好处:

? SQL语句编写保持一致性; ? 提高代码的可读性和可维护性; ? 提高SQL语句执行的效率; ? 易于代码的回顾。

1.3. 参考文献

? ORACLE SQL性能优化系列

2. SQL编码规范

2.1. 查询SQL语句

1) 不等于统一使用\:

Oracle认为“!=”和“<>”是等价的,都代表不等于的意义。为了统一,不等于一律使用“<>”表示。

2) 使用表的别名(Alias):

多表连接时,应为每个表使用别名,别名要简短最好一个字母,且能代表一定意义,所有被引用列要加上表的别名。

当在SQL语句中连接多个表时,请使用表的别名并把别名前缀于每个Column上。可减少解析的时间同时减少那些由Column歧义引起的语法错误。 3) 使用SELECT语句时,必须指出列名:

不要使用列的序号或者用“*”替代所有列名。

?SSC

- 5 -

SQL编码规范建议(Oracle)

当在SELECT子句中列出所有的列COLUMN时,使用动态SQL列引用‘*’是一个方便的方法,但这是一个非常低效的方法。实际上,ORACLE在解析的过程中,会将‘*’依次转换成所有的列名,这个工作是通过查询数据字典完成的,这意味着将耗费更多的时间。 4) 减少子查询的使用:

子查询除了可读性差之外,还在一定程度上影响了SQL运行效率。 请尽量减少子查询的使用,采用其他效率更高、可读性更好的方式替代。 5) 适当添加索引以提高查询效率:

适当添加索引可以大幅度的提高检索速度。同时不要在WHERE字句中对索引列执行函数操作,同时避免隐式转换。 6) 用EXISTS替代IN:

在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接。在这种情况下,使用EXISTS(或NOT EXISTS)通常提高查询的效率。 7) 用NOT EXISTS替代NOT IN:

在子查询中,NOT IN子句将执行一个内部的排序和合并。无论在哪种情况下,NOT IN都是最低效的(因为它对子查询中的表执行了一个全表遍历,然后进行过滤)。为了避免使用NOT IN,可以把它改写成外连接(Outer Joins)或NOT EXISTS。 8) 计算记录条数:

对索引列的计数是最快的,例如:COUNT(EMPNO)。 9) 用表连接替换EXISTS:

通常来说,采用表连接的方式比EXISTS更有效率。 例如: 低效:

SELECT ENAME FROM EMP E

WHERE EXISTS (SELECT ‘X' FROM DEPT

WHERE DEPT_NO = E。DEPT_NO AND DEPT_CAT = ‘A'); 高效:

SELECT ENAME

FROM DEPT D,EMP E

WHERE E。DEPT_NO = D。DEPT_NO AND DEPT_CAT = ‘A' ; 10) 用EXISTS替换DISTINCT:

当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT。一般可以考虑用EXIST替换。

例如:

?SSC

- 6 -

SQL编码规范建议(Oracle)

低效:

SELECT DISTINCT DEPT_NO,DEPT_NAME FROM DEPT D,EMP E

WHERE D。DEPT_NO = E。DEPT_NO 高效:

SELECT DEPT_NO,DEPT_NAME FROM DEPT D

WHERE EXISTS ( SELECT ‘X' FROM EMP E WHERE E。DEPT_NO = D。DEPT_NO); EXISTS使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后立刻返回结果。

11) 用Where子句替换HAVING子句:

避免使用HAVING子句,HAVING只会在检索出所有记录之后才对结果集进行过滤。这个处理需要执行排序、总计等操作。如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销。

例如: 低效:

SELECT REGION,AVG(LOG_SIZE) FROM LOCATION GROUP BY REGION

HAVING REGION != ‘SYDNEY’ AND REGION != ‘PERTH’ 高效:

SELECT REGION,AVG(LOG_SIZE) FROM LOCATION

WHERE REGION != ‘SYDNEY’ AND REGION != ‘PERTH’ GROUP BY REGION

2.2. DML操作

DML操作主要包括数据新增、修改和删除。 1) 使用INSERT语句时,必须指定插入的字段名。

2) 删除重复记录最高效的方法为使用ROWID,如下:

DELETE FROM EMP E

WHERE E。ROWID > (SELECT MIN(X。ROWID) FROM EMP X

WHERE X。EMP_NO = E。EMP_NO);

3) 尽量多使用COMMIT:

?SSC

- 7 -

SQL编码规范建议(Oracle)

在程序中尽量多使用COMMIT,这样程序的性能得到提高,需求也会因为COMMIT所释放的资源而减少。

COMMIT所释放的资源: ? ? ? ?

回滚段上用于恢复数据的信息; 被程序语句获得的锁; redo log buffer 中的空间;

ORACLE为管理上述3种资源中的内部花费。

当删除表中的所有记录时,在通常情况下,回滚段(rollback segments )用来存放可以被恢复的信息。如果你没有COMMIT事务,ORACLE会将数据恢复到删除之前的状态(准确地说是恢复到执行删除命令之前的状况)。

而当运用TRUNCATE时,回滚段不再存放任何可被恢复的信息,当命令运行后,数据不能被恢复。因此很少的资源被调用,执行时间也会很短。但由于不记录日志请酌情使用。

4) 用TRUNCATE替代DELETE来删除全表记录:

2.3. 通用规范

1) 不要使用数据库的类型自动转换功能,使用显式的类型转换; 2) 应使用变量绑定实现SQL语句共享,避免使用硬编码。

2.4. 共享SQL语句

为了不重复解析相同的SQL语句,在第一次解析之后,ORACLE将SQL语句存放在内存中。这块位于系统全局区域SGA(system global area)的共享池(shared buffer pool)中的内存可以被所有的数据库用户共享。因此,当执行一个SQL语句(有时被称为一个游标)时,如果它和之前的执行过的语句完全相同,ORACLE就能很快获得已经被解析的语句以及最好的执行路径。

ORACLE的这个功能大大地提高了SQL的执行性能并节省了内存的使用。当提交一个SQL语句,ORACLE会首先在这块内存中查找相同的语句。

注意:ORACLE对两者采取的是一种严格匹配,要达成共享,SQL语句必须完全相同(包括空格、换行、大小写等)。

共享的语句必须满足三个条件:

a) 字符级的比较:当前被执行的语句和共享池中的语句必须完全相同:

例如:

SELECT * FROM EMP;

SELECT * from EMP; Select * From Emp; SELECT * FROM EMP;

上例中的每条SQL都不能达成共享。 b) 两个语句所指的对象必须完全相同

?SSC

- 8 -

SQL编码规范建议(Oracle)

例如:

用户 对象名 如何访问 Jack sal_limit private synonym Jack Work_city public synonym Jack Plant_detail public synonym Jill sal_limit private synonym Jill Work_city public synonym Jill Plant_detail table owner

考虑以下下列SQL语句能否在这两个用户(Jack, Jill)之间共享: SQL: SELECT MAX(sal_cap) FROM sal_limit; 能否共享: 不能 原因: 每个用户都有一个private synonym - sal_limit , 它们是不同的对象。 SQL: SELECT COUNT(0) FROM work_city WHERE sdesc LIKE 'NEW%'; 能否共享: 能 原因: 两个用户访问相同的对象public synonym - work_city。 SQL: SELECT a。sdesc,b。location FROM work_city a,plant_detail b WHERE a。city_id = b。city_id 能否共享: 不能 原因: 用户jack 通过public synonym访问plant_detail 而jill 是表的所有者,对象不同。

c) 应使用变量绑定实现SQL语句共享,避免使用硬编码。执行相同操作的SQL语句必须

使用相同名字的绑定变量。

例如:第一组的两个SQL语句,绑定变量是相同的,而第二组中的两个语句绑定变量不同,即使赋于不同的绑定变量相同的值也不能使这两个SQL语句相同,达不到共享SQL语句目的。

第一组 select pin,name from people where pin =:blk1。pin; select pin,name from people where pin =:blk1。pin; 能否共享:能 第二组 select pin,name from people where pin =:blk1。ot_ind; select pin,name from people where pin =:blk1。ov_ind; 能否共享:不能 3. 性能优化

3.1. 执行计划分析SQL性能

EXPLAIN PLAN是一个很好的分析SQL语句的工具,它可以在不执行SQL的情况下分析语句。通过分析,就可以知道ORACLE是怎样连接表,使用什么方式扫描表(索引扫描或全表扫描),以及使用到的索引名称,按照从里到外,从上到下的次序解读分析的结果。

?SSC

- 9 -

SQL编码规范建议(Oracle)

EXPLAIN PLAN的分析结果是用缩进的格式排列的,最内部的操作将最先被解读,如果两个操作处于同一层中,带有最小操作号的将首先被执行。

NESTED LOOP是少数不按照上述规则处理的操作,正确的执行路径是检查对NESTED LOOP提供数据的操作,其中操作号最小的将被最先处理。

目前许多第三方的工具如PLSQL Developer和TOAD等都提供了极其方便的EXPLAIN PLAN工具。或者用下列SQL工具找出低效SQL:

SELECT EXECUTIONS,DISK_READS,BUFFER_GETS,

ROUND((BUFFER_GETS-DISK_READS)/BUFFER_GETS,2) Hit_radio, ROUND(DISK_READS/EXECUTIONS,2) Reads_per_run, SQL_TEXT FROM V$SQLAREA

WHERE EXECUTIONS>0 AND BUFFER_GETS > 0

AND (BUFFER_GETS-DISK_READS)/BUFFER_GETS < 0。8 ORDER BY 4 DESC;

3.2. 优化器

选用适合的ORACLE优化器,目前ORACLE的优化器共有3种:RULE(基于规则)、COST(基于成本)、CHOOSE(选择性)。

其中CHOOSE仅在9i及之前的版本被支持,10g已经废除。8i及9i中为默认值。RULE仅在9i及之前版本中被支持,10g已经废除。COST在10g及以后的版本被支持同时为默认值。

3.3. 语句顺序(RBO中有效)

1) 选择最有效率的表名顺序

ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名。因此FROM子句中写在最后的表(基础表 driving table)将被最先处理。 在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表。

当ORACLE处理多个表时, 会运用排序及合并的方式连接它们。首先,扫描第一个表(FROM子句中最后的那个表)并对记录进行派序,然后扫描第二个表(FROM子句中最后第二个表),最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并。

例如:

表 TAB1 16384 条记录,表 TAB2 1 条记录。 1. 选择TAB2作为基础表 (最好的方法) select count(*) from tab1,tab2

执行时间0.96秒。

2. 选择TAB2作为基础表 (不佳的方法) select count(*) from tab2,tab1

执行时间26.09秒。

如果有3个以上的表连接查询,那就需要选择交叉表(intersection table)作为基础表,交

?SSC

- 10 -

SQL编码规范建议(Oracle)

叉表是指那个被其他表所引用的表。

例如:

表EMP为表CATEGORY以及表LOCATION的交叉表。 高效: SELECT * FROM LOCATION L , CATEGORY C, EMP E WHERE E.EMP_NO BETWEEN 1000 AND 2000 AND E.CAT_NO = C.CAT_NO AND E.LOCN = L.LOCN

低效: SELECT * FROM EMP E, LOCATION L, CATEGORY C WHERE E.CAT_NO = C.CAT_NO AND E.LOCN = L.LOCN AND E.EMP_NO BETWEEN 1000 AND 2000

2) WHERE子句中的连接顺序

ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前,那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾。

例如: 低效:

SELECT … FROM EMP E

WHERE SAL > 50000 AND JOB = ‘MANAGER’

AND 25 < (SELECT COUNT(*) FROM EMP WHERE MGR=E.EMPNO); 高效:

SELECT … FROM EMP E

WHERE 25 < (SELECT COUNT(*) FROM EMP WHERE MGR=E.EMPNO) AND SAL > 50000

AND JOB = ‘MANAGER’;

3.4. 减少数据库或表访问次数

当执行每条SQL语句时,ORACLE在内部执行了许多工作:解析SQL语句,估算索引的利用率,绑定变量,读数据块等等。由此可见,减少访问数据库或表次数,就能实际上减少ORACLE的工作量。

?SSC

- 11 -

SQL编码规范建议(Oracle)

1) 整合简单、无关联的数据库访问:

如果有几个简单的数据库查询语句,可以把它们整合到一个查询中(即使它们之间没有关系)

例如:

SELECT NAME FROM EMP

WHERE EMP_NO = 1234;

SELECT NAME FROM DPT

WHERE DPT_NO = 10 ;

SELECT NAME FROM CAT

WHERE CAT_TYPE = ‘RD’;

上面的3个查询可以被合并成一个: SELECT E.NAME,D.NAME,C.NAME FROM CAT C,DPT D,EMP E,DUAL X

WHERE NVL(‘X‘,X.DUMMY) = NVL(‘X‘,E.ROWID(+)) AND NVL(‘X‘,X.DUMMY) = NVL(‘X‘,D.ROWID(+)) AND NVL(‘X‘,X.DUMMY) = NVL(‘X‘,C.ROWID(+)) AND E.EMP_NO(+) = 1234 AND D.DEPT_NO(+) = 10 AND C.CAT_TYPE(+) = ‘RD’; 2) 减少对表的查询:

在含有子查询的SQL语句中,要特别注意减少对表的查询 例如:

低效:

SELECT TAB_NAME FROM TABLES

WHERE TAB_NAME = ( SELECT TAB_NAME FROM TAB_COLUMNS WHERE VERSION = 604)

AND DB_VER= ( SELECT DB_VER FROM TAB_COLUMNS WHERE VERSION = 604) 高效: SELECT TAB_NAME FROM TABLES WHERE (TAB_NAME,DB_VER) = ( SELECT TAB_NAME,DB_VER) FROM TAB_COLUMNS WHERE VERSION = 604) ?SSC

- 12 - SQL编码规范建议(Oracle)

3) Update 多个Column 例子:

低效:

UPDATE EMP

SET EMP_CAT = (SELECT MAX(CATEGORY) FROM EMP_CATEGORIES), SAL_RANGE = (SELECT MAX(SAL_RANGE) FROM EMP_CATEGORIES) WHERE EMP_DEPT = 0020; 高效:

UPDATE EMP

SET (EMP_CAT,SAL_RANGE)

= (SELECT MAX(CATEGORY),MAX(SAL_RANGE) FROM EMP_CATEGORIES) WHERE EMP_DEPT = 0020;

3.5. 函数使用

1) 使用DECODE函数来减少处理时间:

使用DECODE函数可以避免重复扫描相同记录或重复连接相同的表。 例如:

SELECT COUNT(*),SUM(SAL) FROM EMP

WHERE DEPT_NO = 0020

AND ENAME LIKE ‘SMITH%’;

SELECT COUNT(*),SUM(SAL) FROM EMP

WHERE DEPT_NO = 0030

AND ENAME LIKE ‘SMITH%’; 使用DECODE函数整合SQL语句:

SELECT COUNT(DECODE(DEPT_NO,0020,'X',NULL)) D0020_COUNT, COUNT(DECODE(DEPT_NO,0030,'X',NULL)) D0030_COUNT, SUM(DECODE(DEPT_NO,0020,SAL,NULL)) D0020_SAL, SUM(DECODE(DEPT_NO,0030,SAL,NULL)) D0030_SAL FROM EMP

WHERE ENAME LIKE ‘SMITH%';

相同原理DECODE函数也可以运用于GROUP BY 和ORDER BY子句中。

2) 通过内部函数提高SQL效率:

SELECT H.EMPNO,E.ENAME,H.HIST_TYPE,T.TYPE_DESC,COUNT(*) FROM HISTORY_TYPE T,EMP E,EMP_HISTORY H WHERE H.EMPNO = E.EMPNO AND H.HIST_TYPE = T.HIST_TYPE

GROUP BY H.EMPNO,EENAME,H.HIST_TYPE,T.TYPE_DESC; 通过调用下面的函数可以提高效率:

?SSC

- 13 -

SQL编码规范建议(Oracle)

FUNCTION LOOKUP_HIST_TYPE(TYP IN NUMBER) RETURN VARCHAR2 AS

TDESC VARCHAR2(30); CURSOR C1 IS

SELECT TYPE_DESC FROM HISTORY_TYPE

WHERE HIST_TYPE = TYP; BEGIN OPEN C1;

FETCH C1 INTO TDESC; CLOSE C1;

RETURN (NVL(TDESC,’?’)); END;

FUNCTION LOOKUP_EMP(EMP IN NUMBER) RETURN VARCHAR2 AS

ENAME VARCHAR2 (30); CURSOR C1 IS SELECT ENAME FROM EMP

WHERE EMPNO=EMP; BEGIN OPEN C1;

FETCH C1 INTO ENAME; CLOSE C1;

RETURN (NVL(ENAME,’?’)); END;

SELECT H.EMPNO,LOOKUP_EMP(H.EMPNO),

H.HIST_TYPE,LOOKUP_HIST_TYPE(H.HIST_TYPE),COUNT(*) FROM EMP_HISTORY H

GROUP BY H.EMPNO,H.HIST_TYPE;

3.6. 索引

索引是表的一个概念部分,用来提高检索数据的效率。实际上,ORACLE使用了一个复杂的自平衡B-tree结构。通常,通过索引查询数据比全表扫描要快。当ORACLE找出执行查询和Update语句的最佳路径时,ORACLE优化器将使用索引。同样在联结多个表时使用索引也可以提高效率。另一个使用索引的好处是,它提供了主键(primary key)的唯一性验证。

1) 利用索引提高SQL语句执行效率:

除了那些LONG或LONG RAW数据类型,可以索引几乎所有的列。通常,在大型表

?SSC

- 14 -

SQL编码规范建议(Oracle)

中使用索引特别有效。当然,在扫描小表时,使用索引同样能提高效率。

虽然使用索引能得到查询效率的提高,但是我们也必须注意到它的代价。索引需要空间来存储,也需要定期维护,每当有记录在表中增减或索引列被修改时, 索引本身也会被修改。 这意味着每条记录的INSERT、DELETE、UPDATE将为此多付出4到5倍的磁盘I/O,因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢。

2) 定期重构索引:

当对表进行DML操作过多后,会造成索引松散导致进行索引扫描时消耗更多的资源,定期重构索引同样能提高查询效率,重构索引语句如下:

ALTER INDEX REBUILD ;

3) 避免索引失效:

以下操作会导致索引失效:

? Where子句中没有使用索引列; ? Where子句中没有使用前导列; ? 对索引列执行函数操作; ? 对索引列进行运算; ? 隐式类型转换; ? 查询范围过大; ? 统计信息不真实; ? 提示不使用索引。

3.7. 其他

1) 避免使用耗费资源的操作:

带有DISTINCT、UNION、MINUS、INTERSECT、ORDER BY、Group By的SQL语句会启动SQL引擎。

执行耗费资源的排序(SORT)功能。 DISTINCT需要一次排序操作, 而其他的至少需要执行两次排序。

例如,一个UNION查询,其中每个查询都带有GROUP BY子句, GROUP BY会触发嵌入排序(NESTED SORT);因此每个查询需要执行一次排序,然后在执行UNION时,又一个唯一排序(SORT UNIQUE)操作被执行而且它只能在前面的嵌入排序结束后才能开始执行。 嵌入的排序的深度会大大影响查询的效率。

通常UNION、MINUS、INTERSECT的SQL语句都可以用其他方式重写。 注:

如果您数据库的SORT_AREA_SIZE调配得好,使用UNION、MINUS、INTERSECT也可使用,因为此类SQL语句可读性交强。

2) 使用显式的游标(CURSORs)

使用隐式的游标,将会执行两次操作,第一次检索记录,第二次检查TOO MANY ROWS 这个EXCEPTION,而显式游标不执行第二次操作。

?SSC

- 15 -

SQL编码规范建议(Oracle)

3) 分离表和索引

总是将的表和索引建立在不同的表空间内(TABLESPACES),决不要将不属于ORACLE内部系统的对象存放到SYSTEM表空间里。同时,确保数据表空间和索引表空间置于不同的硬盘上。

?SSC

- 16 -

SQL编码规范建议(Oracle)

3) 分离表和索引

总是将的表和索引建立在不同的表空间内(TABLESPACES),决不要将不属于ORACLE内部系统的对象存放到SYSTEM表空间里。同时,确保数据表空间和索引表空间置于不同的硬盘上。

?SSC

- 16 -

本文来源:https://www.bwwdw.com/article/jg2x.html

Top