.Net程序员学用Oracle系列(3):数据库编程规范

《.Net程序员学用Oracle系列:导航目录》

本文大纲

作为一名使用 Oracle 的开发人员,在写这个系列博文之前,我原打算仅对常用 DDL 和 DML 做一个总结就算了。但在本人阅读《剑破冰山——Oracle开发艺术》第一章——《大话数据库编程规范》时,对书中内容产生了强烈的共鸣,因此决定先写 数据库编程规范。本文核心内容来自该书第一章,有删改,且增加了一些本人对书中内容的理解和实际开发中的经验之谈。

书写规范

良好的书写规范给人以享受和艺术的体验,而糟糕的书写不仅可读性差,还给人敬而远之的感觉。数据库编程作为应用程序开发的一部分,也应当要有相应的书写规范。

大小写风格

  • 所有数据库关键字和保留字均使用大写。

我相信一定会有人对这个规范持怀疑态度!我也曾为 SQL 语句到底该用大写还是用小写纠结了好几年!在学习和工作中我先后使用过大小写混合、全小写、全大写等风格,严格来说,没有任何一种是在不同场景下都绝对最好的风格,但综合来讲,数据库关键字和保留字全大写是一种相对较好的风格。

在我学习数据库编程时,我的老师用的是帕斯卡命名法,这种命名书写方便、阅读美观,于是我延续了老师的风格,并沿用到工作中,从未感觉有任何不妥。直到我第一次使用 Oracle,我用标准的帕斯卡命名法写好了表结构定义,执行成功之后,打开设计器看到表结构那一刻,我的眼神是无辜的、内心是崩溃的!因为表名和字段名全都变成了大写,那时我看着顺眼的只有帕斯卡命名法命名的表和字段。向来不愿主动放弃的我觉得一定有办法让 Oracle 支持帕斯卡命名法,还是菜鸟一个的我折腾了半天最后还是不了了之,这令我久久不能释怀,我也因此曾一度非常厌恶 Oracle。

在 Oracle 中,表名和字段名是会被彻底的强制转成大写的,而诸如:视图、函数、存储过程、触发器等对象,虽然通过工具查看时对象名也是大写的,但 DDL 语句中的名称却会保持原来的大小写。而任务的 DDL 语句甚至会被强制转成小写的。

实际上,Oracle 的表名和字段名也是有办法支持大小写的。只需要在创建表的时候,把表名和字段名用引号包裹起来,就可以任意选择帕斯卡命名法和骆驼命名法了。但问题是访问表或字段时也得用引号包裹起来才能访问,否则直接报找不到对象之类的错误。而实际项目中我从未见人这么用过,貌似大部分人都不知道这种怪异的用法!

缩进风格

  • 程序块严格采用缩进风格书写,保证代码清晰易读,风格一致,统一缩进 2/4 个空格。

程序块主要是指表结构、视图、函数、存储过程等数据库对象定义中的程序块。缩进必须使用空格,不允许使用 Tab 键。以免在用不同编辑器阅读程序时,因 Tab 键所设置的空格数目不同而造成程序布局不整齐。

  • 当一条语句过长需要占用多行时,每行开头的关键字与第一行的关键字进行对齐。

书中说应该进行严格的右对齐,但我本人还是喜欢严格的左对齐,毕竟一般编程语言都是提倡左对齐,而且左对齐也更容易书写和控制。我个人觉得换行关键字左对齐还是右对齐都 OK,但必须是严格对齐。

左对齐代码示例

IF v_staff_name IS NULL THEN
  SELECT t.staff_name           -- 同上一行相比缩进 2 个空格
  INTO v_staff_name             -- INTO 与 SELECT 进行左对齐
  FROM t_staff t                -- FROM 与 SELECT 进行左对齐
  WHERE t.staff_id=:p_staff_id; -- WHERE 与 SELECT 进行左对齐
END IF;

右对齐代码示例

IF v_staff_name IS NULL THEN
  SELECT t.staff_name            -- 同上一行相比缩进 2 个空格
    INTO v_staff_name            -- INTO 与 SELECT 进行右对齐
    FROM t_staff t               -- FROM 与 SELECT 进行右对齐
   WHERE t.staff_id=:p_staff_id; -- WHERE 与 SELECT 进行右对齐
END IF;

换行

  • 不允许把多个语句写在一行中,即一行只写一条语句。
  • 避免将复杂的 SQL 语句写成一行,建议在关键字和谓词处换行。
  • 相对独立的程序块之间必须加空行。BEGIN、END 等指令独立成行。
  • 太长的表达式应在低优先级操作符处换行,操作符或关键字放在新行之首。划分出新行应适当缩进,使整个语句排列整齐。
  • 当多个不同类型操作符混合使用时,应当用括号进行隔离区分,使得代码更加清晰易懂。

其它

  • 避免在发布代码中使用 SELECT * 语句,不要用 * 来代替所有字段,应当给出字段列表,以免在表结构发生变化时应用程序出现无法识别等错误情况。
  • 在发布代码中 INSERT 语句必须给出字段列表,以免在表结构发生变化时发生编译错误。
  • 当一个 PLSQL 或 SQL 语句中涉及多个表时,始终使用别名来限定表名和字段名,这样其他人阅读起来更方便,避免了含义模糊的引用,并能够在别名中清晰地判断出表名和相关字段。

凡是将来有可能涉及多张表的 SQL 语句,即使当下只有一个表也最好取个别名,且访问表中任何字段都用表的别名来限定,不要在一条语句中反复使用某个别名。建议养成总是给表取别名且用表的别名来限定表中字段名的好习惯,这可能会给你节省大量时间,也可能会带来意想不到的惊喜。

本人工作中曾遇到写在后端程序中的一条 SQL 语句在某个测试环境中运行正常,但在另一个测试环境中就有问题,忘记了是会报错还是查不到数据,把那条 SQL 语句拷贝到开发环境中运行也没问题。由于那条语句较长,涉及多个表且包含子查询,查询列表的字段超过 20 个,条件中涉及的字段也很多,有些表有别名,有些表又没有,部分字段有表别名限定,部分有表别名的字段又没限定,还有一些没有表别名的字段自然也就没有限定了,结果看了老半天,眼睛都看花了也找不出问题来。无奈之下我把那条语句照抄了一遍,但我给每个表都取了别名,无论查询列表还是过滤条件中的字段我都有用表别名来限定,原本我只是想按照我的习惯把语句抄写整理一遍,方便进一步查找问题,结果写好之后一测试,开发环境及所有测试环境都 OK 了!

  • 确保变量和参数在类型和长度上与表数据列相匹配。以免当较宽或较大的数据传进来时产生异常。
  • 减少控制语句的检查次数,例如 IF...ELSE 控制语句中,应将最常用的符合条件前置。

命名规范

命名规范想要做到完全一致几乎是不可能的,但我们仍要尽量去遵守,必要的时候通过代码审查和专家评审来进行约束,因为一个不成熟的规范总好过没有规范。

数据库对象命名

  • 以下是常见的几种数据库命名风格
    • 1、ProjectName/projectName:这是我最喜欢的命名风格,而且一般编程语言也都提倡这类命名风格。书中说这种适合那些英文比较好,并且喜欢抑扬顿挫和有艺术美感的人。如果你用的不是 Oracle,这种命名也许是最好的选择,即使在 Oracle 中你也能在表结构定义之外地方使用这种命名,但问题是 Oracle 编程过程中几乎所有语句都是在操作表和字段。我个人认为除非你打算同时采用多个命名风格,否则单单这一种命名风格用在 Oracle 中不太合适。
    • 2、PROJECTNAME/projectname:我尝试过这种命名风格,感觉名称短的时候还好,要是名称比较长,尤其是大写,写起来费劲,看起来也费劲。书中说这种适合那些英文好,且比较严禁的人。可能还是我英文太差吧,至少我以后不会再大量使用这种命名了。
    • 3、PROJECT_NAME/project_name:书中说这种适合那些做开发的人,的确开发人员命名时往往会习惯性的加上前缀、后缀或下划线等。应该说我并非特别喜欢这种命名,但如果团队没有其它要求或约束的情况下,我会首选这种命名来进行 Oracle 编程。
    • 4、XMMC/xmmc/XM_MC/xm_mc:书中说这种适合热爱中文的人,前提是恐怕你得对这些缩写先做好相关备注,等大家习惯了才行。首先我肯定是个热爱中文的人,但也许是我被这种命名坑的太多太狠了,所以情感上我特别厌恶这种命名。有些行业的数据库国标或省标也是这类命名,大家基于同样的标准用起来也还好。我曾接手过的一个项目没有这些标准约束,结果就是各种奇葩的命名,给我熟悉这个项目带来了较大的困难。举个实际的例子,项目里有个“内部合同编号”,后来我发现 HTBH、HT_BH、HT_NBBH、NB_HTBH、CONTRACT_BH、CONTRACT_CODE 这些缩写都有可能是这个意思。所以如果你的项目所属行业没有国标、省标等数据库标准,建议你最好远离这种风格。

实际上这几种命名风格各有千秋,很难去指责或否定那种不好,完全取决于整个公司或项目中多数人的习惯,没有十全十美的命名规范,只要绝大多数人心甘情愿地去遵循,那就是好的命名规范。

  • 为避免不必要的冲突和麻烦,最好不要用数据库关键字和保留字。

查询 Oracle 中的关键字和保留字的语句如下:

SELECT * FROM v$reserved_words WHERE reserved='Y'; -- reserved='Y' 表示已被完全禁止

实际上 Oracle 是不建议使用 v$reserved_words 表中所有关键字,但这个表里的关键字太多了。

  • 严禁使用带空格的名称来给表或字段命名。在生产数据库脚本并重新加载的时候可能会出现错误而被迫终止。
  • 除数据库名长度为 1~8 个字符外,其余为 1~30 个字符。命名只能使用英文字母、数字和下划线。
  • 其它各种对象的命名与表和字段的命名风格保持一致即可,最好用不同的前缀,采用前缀的方式来命名对象则很容易通过排序对对象进行区分。其它对象命名规范表如下:
对象名 前缀 范例
表(table) t_/tbl_/不加前缀 t_user/tbl_user/user
视图(view) v_/view_ v_user/view_user
物化视图(materialized view) mv_ mv_user
函数(function) f_/fn_/func_ fn_user/func_user
存储过程(procedure) p_/sp_/proc_ sp_user/proc_user
触发器(trigger) trg_ trg_user
包和包体(package&package body) pkg_ pkg_user
类和类体(type&type body) typ_ typ_user
主键(primary key) pk_ pk_user
外键(foreign key) fk_ fk_user
唯一索引(unique index) uk_ uk_user
普通索引(normal index) idx_ idx_user
位图索引(bitmap index) bk_ bk_user
序列(sequence) seq_ seq_user
簇(cluster) c_ c_user
数据库链接(database link)    
同义词(synonym)    

变量命名

  • 所有 PLSQL 中的变量与对象名规则相似,如下表所示:
变量类型 前缀 范例
输入变量 i_/i i_user_id/iuserid
输出变量 o_/o o_user_name/ousername
输入输出变量 io_/io io_user_id/iouserid
普通变量 v_/v v_user_id/vuserid
全局变量 gv_/gv gv_user_id/gvuserid
常量 全大写 pi
游标 cur_ cur_user_info
用户自定义类型 type_ type_user_info
保存点(save point) spt_ spt_user_info
  • 命名不允许使用中文或特殊字符。命名若使用特殊约定或缩写,则要注释说明。
  • 使用有意义、易于记忆、描述性强、简短及唯一的英文单词。保持自己特有的命名风格,不可来回变换。

说明:个人命名风格,在符合所在项目组的命名规则的前提下,才可以使用。

  • 对于变量名,禁止取单个字符(如i/j……),建议除了要有具体含义外,还要能表明变量类型。

说明:在长的 SQL 语句中,变量用单个字符表示,容易写错,而编译器往往检查不出来,有可能因为这个小小的错误而花费大量的时间。

注释规范

注释规范是判断一个开发人员优劣和成熟度的重要标准。一个优秀的研发人员必然是经过深思熟虑之后才洋洋洒洒妙笔生花的,注释的书写体现了一个人思考问题的全过程和步骤。

  • 在一般情况下,源程序有效注释量必须在 30% 左右。

说明:注释的原则是有助于对程序阅读理解,在该加的地方都加了,注释不易太多也不能太少,注释语言需准确、易懂、简洁、精炼。

  • 所有变量定义都要加注释,说明该变量的用途和含义。
  • 注释内容要清晰、明了、含义准确,防止注释二义性。在代码的功能、意图层次上进行注释,提供有用、额外的信息。避免在一行代码或表达式中间插入注释。尽量使用“--”注释,行尾必须用“--”来注释。
  • 对程序分支必须写注释。在程序块结束行右方加注释,以表明程序块结束。

说明:这些语句往往是程序实现某一特定功能的关键,对于维护人员来说,良好的注释有助于更好的理解程序,有时甚至优于看设计文档。

  • 注释应与其描述的代码相似,对代码的注释应放在其上方或右方(对单条语句的注释)相近未知,不可放在下面。注释要与所描述的代码进行同样的缩排。注释上面的代码最好空行隔开。

语法规范

良好的语法规范有助于书写出高效、完备的 PLSQL 程序,同时有助于提高系统的容错性、健壮性和可追溯性。

  • 避免隐式类型转换。

说明:在书写代码时,必须确定表的结构和表中各个字段的数据类型,特别是在书写查询条件时的字段就更要注意了。这个也是导致 SQL 性能不佳的原因之一。

  • 为了方便不同数据库平台的移植,尽量使用 SQL99 标准,而不要用 Oracle 的“方言”。

例如:DECODE 函数完全可以用 CASE WHIN 语句代替,而且可编程性更强。(+)=右关联用 RIGHT OUTER JOIN 语句代替。=(+)左关联用 LEFT OUTER JOIN 语句代替。

  • 对于非常复杂的 SQL(特别是多层嵌套、带子查询的)语句,应考虑是否由设计不当引起的。对于比较复杂的 SQL 语句可以考虑使用程序实现,原则上遵循一句话只做一件事。

  • 关于 SQL 处理的优先级
    • 1、静态 SQL > 动态 SQL
    • 2、绑定变量的 SQL > 动态 SQL(在 OLTP 系统中建议这么做)
    • 3、SQL > PLSQL 的过程,极端复杂的 SQL 除外
    • 4、SQL > 游标遍历
    • 5、Oracle 函数 > 自定义函数
    • 6、尽量使用 Oracle 分析函数代替同一个表多次的关联。
  • 原则上不要使用动态 SQL,如果非得使用动态 SQL,建议使用绑定变量。
  • 一定要及时关闭和释放游标。
  • 建议在异常处理中,把收集到的错误信息记入错误日志,以备查询和分析。但对于由客户端调用的存储过程,建议不要捕获异常,而是由客户端程序来处理。
  • 不要对空的变量值用比较运算符比较。如果变量可能为空,应使用 IS NULL 或 IS NOT NULL 或 NVL 函数进行比较。
  • 尽可能地使用相关表字段的类型定义,如 %TYPE、%ROWTYPE。这样做当表结构发生变动的时候,能够最大程度的做到容错性和健壮性。
  • 在存储过程中声明变量应集中在 AS 和 BEGIN 关键字之间,不允许在代码中随意定义变量。在定义变量时,完成相同模块功能的变量应放在一起,与不同模块的变量应空行隔开,增加代码的可读性。

总结

正规的 IT 公司一般都会制定一些编程规范,但这其中仍有不少公司只片面的强调前后端编程规范,忽略了数据库编程规范,这既不可取也不合理的。

代码的可读性比说明文档更重要

代码的可读性比说明文档更重要,这是我在工作中总结出的“真理”之一。如果你有被糟糕的程序结构、怪异的变量命名、凌乱的编码风格……折磨过的经历,那么我相信你可能也会有类似的“名句”。

编程规范必不可少

在我学习编程的时候,我的老师就要求我们取变量名时要取有意义的名字,说实在的那时候我还不能意识到这有多重要,值得庆幸的是我记住了老师的话,并落实到学习和实际工作中,就这样在无形中养成了一个好的编程习惯。

在我工作这几年中,接触的很多代码可读性都不高,这些代码的原作者一般也不在公司了,所以往往需要花比较多的时间才能弄懂个大概。比这个还闹心的是,这些代码的可维护性更差,老板可能会想当初那个人做这个功能也就花了两天,给你一天改一下应该是足够的,但他不知道的是一天时间可能你根本没法搞懂那个程序,如果你稀里糊涂的就去改,那结果可能更糟,你会发现随便一改就出来好几个 BUG,然后你再去改 BUG,等你把明显的 BUG 都消灭之后可能已经过去两天了,而且那个程序也被你改的更长更复杂了,往后更加难改了,如果老板再说要改你可能会建议把这个程序扔到垃圾桶,然后自己重新做一个更好的。

如今有大量开发人员在程序的苦海中重复着低级的劳动,其中一个重要原因就是项目当初没有遵从良好的编程规范,导致代码的可读性、可维护性、可移植性、可测试性、开发效率、运行效率等都比较差。所以在正规开发过程中,编程规范是必不可少的!

《.Net程序员学用Oracle系列:导航目录》



posted @ 2017-01-11 18:49 韩宗泽 阅读(...) 评论(...) 编辑 收藏