转载自:http://www.cnblogs.com/leonbao/archive/2008/03/07/1094821.html
有问题的时候,我经常回来博客园寻找答案,久而久之,总结了一些东西。
妄自菲薄,请大家多指出错误,并给出意见
数据库设计三范式基本原则
第一范式:数据库表中的字段都是单一属性的,不可再分。这个单一属性由基本类型构成,包括整型、实数、字符型、逻辑型、日期型等。
也就是说,绝对不要出现下面的情况
学生信息 |
一年一班,97001,张三 |
第二范式:数据库表中不存在非关键字段对任一候选关键字段的部分函数依赖(部分函数依赖指的是存在组合关键字中的某些字段决定非关键字段的情况),也即所有非关键字段都完全依赖于任意一组候选关键字。
也就是说,绝对不要出现下面的情况
学号 | 姓名 | 年龄 | 课程名称 | 成绩 | 学分 |
97001 | 张三 | 13 | 化学 | 88 | 2 |
其中学号和课程名称是联合主键
因为:
(课程名称) → (学分)
(学号) → (姓名, 年龄)
第三范式:在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式。所谓传递函数依赖,指的是如果存在"A → B → C"的决定关系,则C传递函数依赖于A。因此,满足第三范式的数据库表应该不存在如下依赖关系:
关键字段 → 非关键字段x → 非关键字段y
也就是说,绝对不要出现下面的情况
学号 | 姓名 | 年龄 | 所在学院 | 学院地点 | 学院电话 |
97001 | 张三 | 13 | 清华 | 中关村 | 8888888 |
因为:(学号) → (所在学院) → (学院地点, 学院电话)
特别注意:有时为了提高效率,第三范式可以被打破!多见于外键特别多而且数据量巨大的表。为了提高查询的效率,可以牺牲增删改的效率。
关于表、视图、存储过程:
表就是用来存储数据的,要尽量满足三个范式,不要出现冗余的东西。
视图是用来查询数据的,对于没有外键的基础表,可以直接用来查询。对于外键比较多的业务表,查询操作全部要通过视图。
存储过程和触发器我基本不用,我倾向于在数据库层面不要体现太多的业务(甚至不体现),我把业务全部集中在代码层面。其实还有另外一个原因,我不太精通这方面的技术,见谅见谅。
关于索引:
有朋友举过很好的一个例子,聚合索引就像拼音检索,非聚合索引就像部首索引。
拼音索引在整个字典中都是排好序的,就像查英文单词,你只要按照每页角上的英文索引就可以向后翻或者向前翻来找到你想要的单词。但是想象一下,如果造出了一个新单词,再插入字典中,那将是很恐怖的事情,整个该新单词后面的全部单词全都要向后挪,等于字典重新做了。所以,聚合索引对于查找、排序、筛选(比如,我就想看A到C的所有单词)是很方便的事情。
但是,就算有聚合索引,有的SQL关键字也还是要导致全表扫描的,比如说,我要找类似于*ng的单词(LIKE '%ng'),你就要找遍整个字典(全表扫描),但是如果你找类似于ac*的单词(LIKE 'ac%')就很容易。
非聚合索引就是一对一个关系了(非聚合索引就是对应每条内容的地址),你找某一条记录一下就可以定位到。大家可以想想什么样的字段可以作为非聚合索引,没错,一个就是类似于单据号的字段,每条记录都不一样,而且几乎每次都只找一条。还有什么?对,外键,外键都要加非聚合索引,我实际测过,一个表有很多外键,视图中都是用Inner Join连接起来的,相比之下,10万条数据,没有建立非聚合外键索引的情况下,试图打开用12秒,为每个外键建立非聚合索引,打开速度马上缩短到6秒。
我引用联机文档中的原话来描述一下:
在创建非聚集索引之前,应先了解访问数据的方式。考虑对具有以下属性的查询使用非聚集索引:
- 使用 JOIN 或 GROUP BY 子句。
应为联接和分组操作中所涉及的列创建多个非聚集索引,为任何外键列创建一个聚集索引。 - 不返回大型结果集的查询。
- 包含经常包含在查询的搜索条件(例如返回完全匹配的 WHERE 子句)中的列。
关于主键:
我比较倾向于主键的业务无关性,用的是著名的GUID。虽然占用空间较大,效率也偏低,但是在找不出其它更好的方法。
需要注意的是,建立主键时,SQL Server默认会把主键设置为聚合索引,一定要把他去掉,设置在更有意义的其它字段上,或者压根就不设。
GUID的好处很多,有:
生成主键简单,可预知。
没有并发时主键重复的烦恼。
防止用户手动更改数据库中的数据,一看到GUID,就都吓回去了。
避免数据库表迁移时的麻烦(用自增型的主键,在表迁移时简直就是灾难)。
避免了基础表更新时外键的级联更新(主要体现在主键业务无关性上)。
欢迎大家多提意见。