参考的优秀文章
数据库设计是件严肃、关键的事儿,一毕业,加入一个大型的行业项目,那儿的前辈资深工程师,就给我灌输数据库如何关键、神圣、深不可测的观念,所以,我一直怀着崇拜的眼神。
几年前,项目经理把一个小项目的数据库设计工作交给我,我除了花费晚上和周末去完成。后来,更由于第一次负责整个系统的数据库设计,更请教了以前公司的架构师哥们,帮我把把关,看自己有哪些木有想到的。
后来,将设计方案通过了评审,甚是高兴,毕竟自己第一次设计一个系统的表结构,尽管,是一个小系统。
那么,有了几次数据库表结构设计经验,如何描述你设计表所遵循的原则呢?这时候,回归到大学课本,就是范式(Normal Form)。
第一范式:原子性,不可再分
原子性,即,字段应该是不可再分的。
是否为原子性
如,一个系统的地址用于邮寄商品,那么下表的ADDRESS是符合第一范式的。
ID | USER_NAME | AGE | PHONE | ADDRESS |
1 | Nick Huang | 18 | 12345678 | 深圳市罗湖区地王大厦1003室 |
而如何判断是否原子性,这需要根据实际的业务判断。
比如一些系统仅根据地址作送货服务的,则使用上述的结构即满足第一范式;而某些系统,地址除了用于送货,还需要对用户所在地区分布做长期的统计,为了统计方便,上面的ADDRESS设计就不符合原子性了,也许应该为:
ID | USER_NAME | AGE | PHONE | PROVINCE | CITY | ADDRESS |
1 | Nick Huang | 10 | 12345678 | 广东省 | 深圳市 | 地王大厦1003室 |
典型的例子:多个信息用分隔符拼接记录
还有其他一些典型的不符合原子性的,就是将多个数据放在一个字段中了。
如Phone字段:
ID | USER_NAME | AGE | PHONE |
1 | Nick Huang | 10 | 23658745,25654150 |
还有这种情况,如EXT_FIELDS字段:
ID | USER_NAME | AGE | PHONE | ADDRESS | EXT_FIELDS |
1 | Nick Huang | 10 | 12345678 | 深圳市XXX | <DATA><JOB>Programmer</JOB><PASSPORT>12345678</PASSPORT></DATA> |
第二范式:非主键必须完全依赖于主键,而不能只依赖于主键的一部分
非主键必须完全依赖于主键,而不能只依赖于主键的一部分(联合主键)
不符合此特性的示例
博文《权限管理系统概要设计》有一系列用户权限的表,如果以此为例子,将其中的表结构设计为如下,则不符合第二范式:
USER_ID、ROLE_ID为主键,描述用户和角色的关联关系;STATUS描述这个关联关系是生效还是失效。
可以看出,STATUS是描述这段关联的,是依赖USER_ID、ROLE_ID的,即完全依赖于主键。
而USER_NAME是用户名称,它只依赖于USER_ID,即只依赖于主键的一部分。USER_NAME字段的设置不符合第二范式。
如果有一天,用户的名称需要修改,那么就要修改与此用户相关的每一笔关联的数据。
第三范式:非主键必须直接依赖于主键,而不是传递依赖或间接依赖
非主键必须直接依赖于主键,而不是传递依赖或间接依赖。
不符合此特性的示例
博文《权限管理系统概要设计》有一系列用户权限的表,如果以此为例子,将其中的表结构设计为如下,则不符合第三范式:
此为角色表,ID为角色ID,NAME为角色名称,STATUS为此角色是否生效,SYSTEM_ID为此角色所属的系统ID,SYSTEM_NAME为此角色所属的系统的名称。
可以看出SYSTEM_NAME为传递依赖,在角色表中SYSTEM_ID依赖与ID,而SYSTEM_NAME有依赖与SYSTEM_ID。SYSTEM_NAME字段的设置不符合第三范式。
后话
是不是数据库设计一定得严格遵守3范式呢?
这不一定,要视具体情况,实际上,常见许多情况故意设置冗余字段使系统查询更高效、更方便。