1. 编程规范本章讲述ORACLE数据库SQL脚本编码规范。 1.1. 总述1.1.1. 编码规范1)编写脚本时,Oracle保留字大写,其他一律使用小写,除非必要(如:作字符串时或注释语句中); 2)脚本必须规范,SQL编写不能采用缩略写法:如INSERT INTO中的INTO不能省略;INTO后面要列出字段名; 3)语句内的空格,统一为一个空格; 4)表定义中,字段名、字段类型(长度)、缺省、(非)空、字段注释都必须左对齐; 5)使用空格键对齐,不要使用Tab键; 6)运算符前后,各保留一个空格; 7)所有对象命名必须是有意义的,且不能使用保留字; 8) 后台存储过程SQL文件中不允许出现字符’&’ ,注释中也不允许; 1.1.2. 关于注释为保证SQL语句的可读性和推广性,需增加相关注释说明。 包、存储过程、函数的注释中应包含以下内容: 1)编写人、编写日期、编写目的与主要内容; 2)修改人、修改日期、修改目的与主要内容; 3)如果有特殊处理、特别的技巧等内容,一定要在注释中详细说明; 4)注释应单独成行,并放在语句前面; 5)应对不易理解的分支条件表达式加注释; 6)对重要的计算应说明其功能; 7)过长的函数实现,应将其语句按实现的功能分段加以概括性说明; 8)对常量及变量注释时,应注释被保存值的含义,包括合法取值的范围; 9)应采用多行注释方式(/* */ ); 10) 注释尽量采用英文,防止数据库字符集问题; 11)源程序有效注释量必须在30%左右,注释不宜太多也不能太少。 1.2. 对象命名规则Oracle数据库对象一般按照以下原则命名,可适当简化,名字长度不能超过30。 1.2.1. 表数据库表的命名规则: 1)表名一般用T_开头,表示为TABLE; 2)系统表,加上SYS_,如:T_SYS_; 基础表,加上INFO_,如:T_INFO_; 日志表,加上LOG_,如:T_LOG_; 配置表,加上CFG_,如:T_CFG_; 备份表,加上BAK_前缀和时间后缀,如: BAK_T_OTHER_20130507; 临时表,加上TMP_前缀和时间后缀,如:TMP_T_OTHER_20130507; 3)多个单词间以下划线(_)进行连接,表示功能; 如:T_LOG_ONLINE_PAYMENT,表示:在线交易日志表,这样很直观; 4)入内存的表用C_开头,保证开发人员能够将正确的表初始化到应用程序的CACHE; 如:C_INFO_MOBILE_H,这表示:该号段表是放在CACHE使用的。 数据库表的字段命名规则: 1)字段名全部采用英文单词,单词之间用下划线(_)进行连接; 2)如果该字段是编码,则使用_CODE为后缀,如:CUST_CODE 客户编码; 3)序号列使用_ID后缀,如:USER_ID 用户编号; 4) 如果该字段表示的是时间,精确到日的字段,用_DATE为后缀;精确到秒或毫秒的,用_TIME为后缀。如:REGISTER_TIME表示注册时间,OPEN_DATE表示开户日期。 1.2.2. 视图数据库视图的命名规则: 1) 视图名用V_开头,表示VIEW; 2) 视图由几个表产生就用下划线(_)连接几个表的名。 1.2.3. 序列数据库序列的命名规则: 1) 序列名用SEQ_开头,表示SEQUENCE; 2) 之后使用该序列的表名和字段名。 1.2.4. 存储过程存储过程的命名采用如下规则: 1) 存储过程名用PRO_开头,表示PROCEDURE; 2) 之后多个单词以下划线(_)进行连接,表示功能; 3) 输入参数用I_开头,输出参数用O_开头。 1.2.5. 函数函数的命名采用如下规则: 1) 函数名用FUN_开头,表示FUNCTION; 2) 之后多个单词以下划线(_)进行连接,表示功能; 3) 输入参数用I_开头,输出参数用O_开头。 1.2.6. 包1.2.6.1 包的命名采用如下规则: 1) 函数名用PKG_开头,表示PACKAGE; 2) 之后多个单词间以下划线(_)进行连接,表示功能。 1.2.6.2 包中存储过程的命名采用如下规则: 1) 存储过程名用PRO_开头,表示为PROCEDURE; 2) 之后多个单词间以下划线(_)进行连接,表示功能。 3) 输入参数用I_开头,输出参数用O_开头。 1.2.6.3包中函数的命名采用如下规则: 1) 函数名用FUN_开头,表示为FUNCTION。 2) 之后多个单词间以下划线(_)进行连接,表示功能。 3) 输入参数用I_开头。 1.2.7. 触发器触发器的命名规则: 1) 触发器名用TRI_开头,表示TRIGGER; 2) 基本部分,描述触发器所加的表; 3) 后缀(_I、_U、_D),表示触发条件的触发方式(INSERT, UPDATE或DELETE); 1.2.8. 主键主键的命名规则: 主键名用PK_开头,后面紧跟该主键所在的表名(不含T_)。 1.2.9. 外键外键的命名规则: 外键名用FK_开头,后面紧跟该外键所在的表名和对应的主表名(不含T_)。子表名和父表名用下划线(_)分隔。 1.2.10. 索引索引的命名规则: 1) 主键对应的索引和主键同名; 2) 一般索引都以I_ 开头,若是唯一索引以IU_开头;若是位图索引以IB_开头。后面紧跟表名(不含T_)和索引所在字段名。 1.2.11. 其他1) 定义变量时,以V_作为前缀,如V_SAL; 2) 定义常量时,以C_作为前缀,如C_RATE; 3) 定义异常时,以E_作为前缀,如E_INTEGRITY_ERROR; 4) 定义游标时,以CURSOR_作为前缀,如CURSOR_ EMP; 5) 定义表类型时,以TABLE_TYPE_作为前缀,如TABLE_TYPE_ SAL; 6) 定义表变量时,以TABLE_作为前缀,如TABLE_ SAL; 7) 定义记录类型时,以RECORD_TYPE_作为前缀; 8) 定义记录变量时,以RECORD_作为前缀。 1.3. SQL代码编写总则1) SQL语句应正确、规范、高效; 2) 代码清晰、整齐,具有可观赏性; 3) 代码编写要充分考虑执行效率最优的原则; 4) 代码行整体层次分明、结构化强; 5) 代码中应有必要的注释以增强代码的可读性; 6) 同一项目的SQL书写格式必须统一; 7) 编写简单易懂的代码;避免复杂的循环、条件; 8) 采用临时表等方式,尽量减少SQL嵌套层级数; 9) 实际应用中,在不违反常规要求的前提下,允许存在可理解的调整。 1.4. SQL代码编写细则1.4.1. 使用相同的SQL语句必须绑定变量当向ORACLE 提交一个SQL语句,ORACLE会首先在共享池中查找相同的语句。需要说明的是,ORACLE对两者采取的是一种严格匹配,要达成共享,SQL语句必须完全相同(包括空格、换行等)。 1.4.2. WHERE条件执行顺序1)表之间的连接必须写在其他WHERE条件之前; 2)可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾。 1.4.3. JOIN的连接顺序1) 使用JOIN ON 代替 WHERE
2) 多表连接的时候,会遇到INNER JOIN与OUTER JOIN的共用,这时必须INNER JOIN在前,OUTER JOIN在后,以减少二次匹配时的结果集
附:通过图形的方式来理解下各个连接之间的差别。 1.4.4. 表连接其他规范1) 通过有索引的列进行表连接; 2) 使用等值连接,不能使用<>, <, >方式的连接; 3) 连接的表不能超过5个;若超过则优化表结构、使用中间临时表等; 4) 多表关联查询,表必须使用别名,列名前必须带表的别名。 1.4.5. 避免SELECT *不能使用SELECT *,应该列出所有的字段COLUMN。
1.4.6. INSERT语句,应指定具体的字段名
1.4.7. 用TRUNCATE替代DELETE当需要删除全表数据时,用TRUNCATE替代DELETE。 DELETE,TRUNCATE都用于删除表中的数据(不删除表结构), DELETE,可以删除整个表的数据也可以删除表中某一条或N条满足条件的数据,而TRUNCATE只能删除整个表的数据,一般我们把DELETE操作叫作删除表,而TRUNCATE操作叫作截断表。 TRUNCATE操作与DELETE操作对比如下:
1.4.8. 避免等待应遵循以下规则1) 在FOR UPDATE 后面加NOWAIT关键字
2) 多表关联查询更新时,要用FOR UPDATE OF table.column NOWAIT,只锁定需要的表
1.4.9. 用WHERE替换HAVINGHAVING 只会在检索出所有记录之后才对结果集进行过滤。因此,除非对聚合函数的结果进行比较,否则条件必须写在WHERE子句中。
1.4.10. 减少对表的查询次数(尽量减少子查询数量)在含有子查询的SQL语句中,要特别注意减少对表的查询次数。
1.4.11. 一次表扫描做尽可能多的操作1) 采用DECODE,CASE WHEN等分支函数; 2) 如果一次扫描既有删除操作也有更新操作,采用MERGE语句等来代替INSERT\UPDATE。 1.4.12. 分离表和索引的表空间1)建表和索引时,必须指定供业务使用的表空间; 2)表和索引的表空间必须分开。 1.4.13. 尽量使用存储过程对于包含多条SQL语句或者需要循环执行多次的SQL语句,多使用存储过程,因为存储过程是预编译的。 1.4.14. 存储过程减少对dual表的调用
1.4.15. 使用%TYPE和%ROWTYPE,更好的适应对象结构的变更1) 定义与表的某个字段相同类型的变量时,必须使用%TYPE; 2) 定义与某个表的数据结构一致时,必须使用%ROWTYPE。 1.4.16. 择优使用EXITST和IN在不导致语义发生变化情况下,必须使用尝试下述两种写法,选最优。
一般情况下,Table1数据量小而Table2数据量非常大时,Table1<<Table2 时,语句一的查询效率高;Table1数据量非常大而Table2数据量小时,Table1>>Table2 时,语句二的查询效率高。 1.4.17. 用NOT EXISTS替代NOT IN1) 结果集区别 ① 对于NOT EXISTS查询,内表条件字段存在空值对查询结果没有影响;对于NOT IN查询,内表条件字段存在空值将导致最终的查询结果为空。 ② 对于NOT EXISTSs查询,外表条件字段存在空值,存在空值的那条记录最终会输出;对于NOT IN查询,外表条件字段存在空值,存在空值的那条记录最终将被过滤,其他数据不受影响。 2) 性能区别: 在子查询中,NOT IN子句将执行一个内部的排序和合并。无论在哪种情况下,NOT IN都是最低效的 (因为它对子查询中的表执行了一个全表遍历)。
1.4.18. 择优使用表连接和EXISTS在不导致语义发生变化情况下,必须尝试以下两种写法,选最优。
1.4.19. 尽量避免使用DISTINCT在不导致语义发生变化情况下,必须使用尝试上述两种写法,选最优。
1.4.20. 尽量在程序中对查询结果进行排序排序比较消耗服务器内存与磁盘I/O,排序应首先考虑再应用程序中进行。 1.4.21. 大批量插入、更新、删除数据时要分批提交大批量插入数据时要分批提交,跳过索引维护插入数据则需要重建索引并更新统计信息,不跳过索引维护则只需要更新统计信息。大批量UPDATE\DELETE语句也要分批提交,每批数据量建议在1万条记录。针对INSERT和UPDATE循环时请注意循环的退出条件,防止出现死循环。ORACLE中可以通过ROWNUM或WHERE条件进行循环,而且每次循环必须COMMIT。 1.4.22. 数据库中不允许有无限增长的表日志表,交易流水表等这类表要定时清理(大表清理机制)。 1.4.23. 不允许表达式中对NULL值(或有可能为NULL)的字段进行直接运算不允许表达式中对NULL值(或有可能为NULL)的字段进行直接运算,必须经过Oracle的NVL函数处理后才能进行运算。在表达式中的值如果为NULL,直接运算将造成结果不可预期。 1.4.24. 联合查询时,尽量使用UNION ALL替代UNION如果可以确认合并的两个结果集中不包含重复的数据或者不关注结果集中是否有重复记录,就使用UNION ALL。 1.4.25. RAC环境上创建SEQUENCE1) 如果业务确实对SEQUENCE的顺序有严格要求,才能使用order选项; 2) CACHE设置为100以上。 1.4.26. 并发处理和事务控制规范1) 不要把大的事务分成若干小事务(除非是分批删除数据); 2) 严禁自动提交事务; 3) 类似的应用逻辑尽量按相同的顺序对记录DML(有效避免死锁); 4) 避免分布式事务(尽量不使用DBLINK); 5) 严禁在代码中使用DDL; 6) 数据库脚本中存储过程的事务,必须能在存储过程执行返回前关闭; 7) SELECT语句要放到事务外,避免事务执行时间过长。 1.4.27. 应按照业务需要使用事务,同时应保持事务简短,避免大事务1.4.28. 在事务完整性的基础上,及时提交事务、释放系统资源SQL语句应在程序中显式使用 COMMIT,ROLLBACK。 1.4.29. SQL语句应避免频繁引起数据库事务回滚业务的SQL语句不能更新某个字段后等待外部系统响应,成功则提交、失败则回滚,这种方式会由于外部系统出错而导致事务频繁回滚。正确处理方式:更新此字段为中间值,外部返回成功则更新为成功值、失败则更新为失败值。 1.4.30. JOB必须精确定时处理定时任务的JOB,INTERVAL参数必须使用TRUNC函数进行精确定时,避免由于JOB执行延迟而累计到JOB的下次执行时间NEXT_DATE。
1.4.31. 尽量使用Oracle时间函数代替时间的拼接尽量使用TRUNC函数实现精确到月、天、时、分、秒的需求。
1.4.32. 查询条件中不能使用”||”拼接两个字段,或者拼接字符和字段
1.4.33. SQL语句应避免删除表数据的操作应用程序SQL语句尽量不要删除表数据,即使必须删除,需经DBA审核,并在删除前备份原表数据。 1.4.34. 应将SQL语句中的数据库函数、计算表达式等放置在等号右边无论等号左边的字段是否为索引字段,一律把函数或计算表达式放置在等号右边。 1.4.35. 必须按照字段的类型,传递相同类型的参数必须按照字段的类型,传递相同类型的参数。如果索引字段(分区字段)是字符类型VARCHAR2,实际传参是数字类型或其他非字符类型,则不能使用索引(分区裁剪),导致性能大大降低。 1.5. 使用索引的规范索引创建规范 索引的创建应慎重考虑,不是越多越好,索引可以提高相应的SELECT的效率,但同时也降低了INSERT及 UPDATE 的效率。 1.5.1. 在频繁进行表关联的字段上建立索引1.5.2. 大表的关联查询字段必须建立索引大表在设计阶段就必须在关联查询字段上创建索引,索引的创建SQL必须发给DBA审核确认。 1.5.3. SQL语句应避免对大表的全表扫描,对大表的操作应尽量使用索引大表在创建时,必须明确该表的数据操作方式;表数据的更新、查询、删除都尽量使用索引字段。 1.5.4. 在频繁ORDER BY / GROUP BY的列上建立索引1.5.5. 索引应建在不频繁修改的字段上1.5.6. 不要建立过多的或重复的索引,删除不使用的索引建议单表索引个数尽量不超过3个,避免数据插入,更新等操作效率降低。 1.5.7. 不要在差异度较小的列上建立普通B树索引索引使用规范 1.5.8. 查询条件必须包含(复合)索引的第一个字段如果索引是建立在多个列上(复合索引),只有当它的第一个列(LEADING COLUMN)被WHERE子句引用时,优化器才会选择使用该索引。当只引用索引的其他列时,优化器则会全表扫描而忽略该索引。查询条件的顺序须与复合索引列的顺序相同。 1.5.9. 避免在WHERE子句中对索引列进行计算
1) 避免在 WHERE 子句索引列上使用函数或者表达式;如果确需使用,应建立对应的函数索引
1.5.10. 索引列比较时,尽量用 >= 替代 >
两者的区别在于,前者将直接跳到第一个DEPT等于4的记录而后者将首先定位到DEPTNO=3的记录并且向前扫描到第一个DEPT大于3的记录。 1.5.11. 避免使用< >, NOT IN比较索引列1.5.12. 避免对索引列使用 IS NULL, IS NOT NULL1.5.13. 索引列LIKE必须正确使用避免在 WHERE 子句中对索引列使用 LIKE ’%xxx%’或’%xxx’,即LIKE的条件中首位不能使用通配符% 。
1.5.14. 用LIKE代替substr索引列
1.5.15. 比较值与索引列数据类型必须一致在WHERE子句中应注意,显式转换比较值使其与索引列数据类型保持一致。 1.5.16. 排序必须基于索引列尽量减少ORDER BY和GROUP BY等排序操作;如果排序,必须在索引列上排序。 1.6. 使用PL/SQL美化SQL1) 使用PL/SQL Developer,在工具栏中找到PL/SQL美化器按钮(PL/SQL Beautifier),按动该按钮,可以按照默认或者自定义的规则美化当前窗口的SQL;如下图 2) PL/SQL美化器可以用在几乎所有SQL程序,包括SQL语句、块、过程、函数以及包,如果窗口只有DDL语句,只要在前面加一句简单的查询(SELECT SYSDATE FROM dual)便可以美化; 3) 如果SQL语句无法美化,PL/SQL窗口会按如下提示: 除了DDL语言和某些特殊情况,无法美化的SQL在结构上一定有问题,一定无法执行或编译,当然PL/SQL美化器不会对语句的逻辑进行检查,因此可美化的SQL也不一定能执行或者编译。 2. 安全规范现网的数据库必须经过以下加固处理,应用程序必须支持这些加固措施: 1) 开发和业务人员的上线SQL和查询数据SQL,需要提交数据库组审核; 2) 修改管理员账号(SYS,SYSTEM)的默认密码,并定期修改管理员密码; 3) 删除或锁定默认账号:如DBSNMP,PERFSTAT等; 4) 监听器加密码; 5) 修改数据信任文件的访问权限,切忌配置成666或777; 6) 非DBA不能直接在生产库上执行查询SQL,防止数据库过载; 7) 创建和重建大表索引必须在业务低峰期处理,防止由于锁表导致应用程序操作数据库时发生等待; 8) 限制数据库超级管理员远程登录; 9) 开发人员申请SEQUENCE改动时,保证调用该SEQUENCE的应用已全部匹配修改(应用字段的长度大于或等于SEQUENCE最大值长度),间接关联的字段传递也必须保证已匹配修改。 posted @ 2016-07-08 11:20 |
|