功能应该由程序实现,而非DB实现。
原因在于,如果功能由DB实现时,一旦更换的DBMS不如之前的系统强大,不能实现某些功能,这时我们将不得不去修改代码。所以,为了杜绝此类情况的发生,功能应该有程序实现,数据库仅仅负责数据的存储,以达到最低的耦合。
当定义一个实体与其他实体之间的关系时,需要考量如下:
- 牵涉到的实体 识别出关系所涉及的所有实体。
- 所有权 考虑一个实体“拥有”另一个实体的情况。
- 基数 考量一个实体的实例和另一个实体实例关联的数量。
描述1:1关系最少需要1张表。
描述1:n关系最少需要2张表。
描述n:n关系最少需要3张表。
如果表示坐标(0,0),应该使用两列表示,而不是将“0,0”放在1个列中。
列的顺序对于表来说无关紧要,但是从习惯上来说,采用“主键+外键+实体数据+非实体数据”这样的顺序对列进行排序显然能得到比较好的可读性。
数据表必须定义主键和外键(如果有外键)。
定义主键和外键不仅是RDBMS的要求,同时也是开发的要求。几乎所有的代码生成器都需要这些信息来生成常用方法的代码(包括SQL文和引用),所以,定义主键和外键在开发阶段是必须的。之所以说在开发阶段是必须的是因为,有不少团队出于性能考虑会在进行大量测试后,在保证参照完整性不会出现大的缺陷后,会删除掉DB的所有外键,以达到最优性能。笔者认为,在性能没有出现问题时应该保留外键,而即便性能真的出现问题,也应该对SQL文进行优化,而非放弃外键约束。
人工健——实体的非自然属性,根据需要由人强加的,如GUID,其对实体毫无意义;自然健——实体的自然属性,如身份证编号。
人工键的好处:
-
键值永远不变
-
永远是单列存储
人工键的缺点:
-
因为人工键是没有实际意义的唯一值,所以不能通过人工键来避免重复行。
笔者建议全部使用人工键。原因如下:
-
在设计阶段我们无法预测到代码真正需要的值,所以干脆放弃猜测键,而使用人工键。
-
人工键复杂处理实体关系,而不负责任何属性描述,这样的设计使得实体关系与实体内容得到高度解耦,这样做的设计思路更加清晰。
笔者的另一个建议是——每张表都需要有一个对用户而言有意义的自然键,在特殊情况下也许找不到这样一个项,此时可以使用复合键。这个键我在程序中并不会使用其作为唯一标识,但是却可以在对数据库直接进行查询时使用。
使用人工键的另一根弊端,主要源自对查询性能的考量,因此选择人工键的形式(列的类型)很重要:
- 自增值类型 由于类型轻巧查询效率更好,但取值有限。
- GUID 查询效率不如值类型,但是取值无限,且对开发人员更加亲切。
智能键——键值包含额外信息,其根据某种约定好的编码规范进行编码,从键值本身可以获取某些信息;非智能键,单纯的无意义键值,如自增的数字或GUID。
智能键是一把双刃剑,开发人员偏爱这种包含信息的键值,程序盼望着其中潜在的数据;数据库管理员或者设计者则讨厌这种智能键,原因也是很显然的,智能键对数据库是潜在的风险。前面提到,数据库设计的原则之一是不要把具有独立意义的值的组合实现到一个单一的列中,应该使用多个独立的列。数据库设计者,更希望开发人员通过拼接多个列来得到智能键,即以复合主键的形式给开发人员使用,而不是将一个列的值分解后使用。开发人员应该接受这种数据库设计,但是很多开发者却想不明白两者的优略。笔者认为,使用单一列实现智能键存在这样一个风险,就是我们可能在设计阶段无法预期到编码规则可能会在后期发生变化。比如,构成智能键的局部键的值用完而引起规则变化或者长度变化,这种编码规则的变化对于程序的有效性验证与智能键解析是破坏性的,这是系统运维人员最不希望看到的。所以笔者建议如果需要智能键,请在业务逻辑层封装(使用只读属性),不要再持久化层实现,以避免上述问题。
关于NULL我们需要了解它的几个特性:
-
任何值和NULL拼接后都为NULL。
-
所有与NULL进行的数学操作都返回NULL。
-
引入NULL后,逻辑不易处理。
那么我们是否应该允许列为空呢?
笔者认为这个问题的答案受到我们的开发语言的影响。以C#为例,因为引入了可空类型来处理数据库值类型为NULL的情形,所以是否允许为空对开发者来说意义并不大。
但有一点必须注意,就是验证非空必须要在程序集进行处理,而不该依赖于DBMS的非空约束,必须确保完整数据(所有必须的属性均被赋值)到达DB(所谓的“安全区”,我们必须定义在多层系统中那些区域得到的数据是安全而纯净的)。
一种错误想法是,属性与列是1:1的关系。
对于开发者,我们公开属性而非字段。举个例子来说,对于实体“员工”有“名字”这一属性,“名字”可以再被分解为“姓”和“名”,对于开发人员来说,显然第二种数据结构更受青睐(“姓”和“名”作为两个字段)。所以,在设计时我们也应该根据需要考虑是否切割属性。
当笔者还在大学时,范式是学习关系型数据库时最头疼的问题。我想也许会有读者仍然不理解范式的价值,简单来说——范式将帮助我们来保证数据的有效性和完整性。规范化的目的如下:
- 消灭重复数据。
- 避免编写不必要的,用来使重复数据同步的代码。
- 保持表的瘦身,以及减从一张表中读取数据时需要进行的读操作数量。
- 最大化聚集索引的使用,从而可以进行更优化的数据访问和联结。
- 减少每张表使用的索引数量,因为维护索引的成本很高。
规范化旨在——挑出复杂的实体,从中抽取出简单的实体。这个过程一直持续下去,直到数据库中每个表都只代表一件事物,并且表中每个描述的都是这件事物为止。