MySQL 表设计

0、这篇博客中一部分内容是Deolin的个人观点和习惯。

1、把SET remove_time = '1970-01-01 00:00:00' 称为“移除”(remove),

把DELETE FROM table 称为“擦除”(expunge),

至于“删除”(delete)、“取消”(cancel),则更像个业务层而不是持久层的概念。

2、数据表可以分为信息表,关联表,字典表。

3、信息表反映的是数据信息,“数据为王”主要指的就是这类表里的数据。

信息表应该有以下字段

id int 这条数据的主键,默认长度,无符号,自增,前位补零。由于remove_time的存在,所以业务上的字段做主键便不再合适,因为新插入的数据很有可能与被移除的数据发生主键重复,所以需要一个毫无业务意义的主键

insert_time datetime 这条数据第一次执行insert文的时间,不能为空。

update_time datetime 这条数据每次执行update文的时间,可以为空(如果从为被update过,那就应该是空的)。

remove_time datetime 这条数据执行remove文的时间,可以为空,这个字段有值则代表这条数据被删了,

由于信息表里的数据往往比较重要,所以应该禁止擦除,使用移除。而“有remove_time字段”和“使用移除”两件事情应该同时出现,或同时不出现

信息表可以有以下字段,但不是“应该有”

insert_id int 插入这条数据的业务上的插入者id,代表的是某一个信息表的主键(user表之类的)。

insert_table varchar(64) 如果业务上不只一类插入者,那么需要指定是那个信息表。长度定为64,因为表名最长64个字符。

update_id int 与insert_id同理。

update_table varchar(64) 与insert_table同理。

remove_id int 与insert_id同理。

remove_table varchar(64) 与insert_table同理。

如果是整个项目在业务上没有登录行为,或是管理员在db上插入/修改的,那么些字段只能为空了,这也是这些字段不算“应该有”的原因了。

一个信息表的示例可能是这样的

CREATE TABLE `person` (
  `id` int(11) unsigned zerofill NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `sex` tinyint(1) DEFAULT NULL,
  `birthday` date DEFAULT NULL,
  `father_age` int(3) DEFAULT NULL, #这个字段可能非常奇怪,但Deolin只是希望能在示例里有至少一个int字段,并且最好不是age(age跟birthday一起出现的话显得更加奇怪)
  `tuition` decimal(8,2) DEFAULT NULL,
  `payment_time` datetime DEFAULT NULL,
  `vip_flag` tinyint(1) DEFAULT NULL,
  `insert_time` datetime NOT NULL,
  `update_time` datetime DEFAULT NULL,
  `remove_time` datetime DEFAULT NULL
  PRIMARY KEY (`id`)
)

4、关联表反映的是两张信息表之间多对多的关系,例如学生表与课程表之间的关系

除了主键,关联表应该有像信息表那样的基础字段。

至于主键,它的设计有两种方式

第一种

放弃主键id,而是使用被关联表各自的id作为双主键(如student_id, lesson_id),

这种方式优点是直观,一眼就能看出是关联表,而且关联关系绝对不会重复。

有个缺点,remove_time和移除操作不能有了,因为会发生主键重复,所以只能擦除。

第二种

使用主键id,优缺点与第一种方式正好相反。

主要适用于两种场合,一是整个项目有比较强的数据挖掘和分析目地的,即便是关联关系也要保留下来,

二是关联关系产生时,会出现一些业务字段(如学生选的课,会有成绩,那么成绩字段不应该放在student或lesson中,而是应该放在关联表里)

5、字典表反映的是为项目本身提供的各种数据,是对项目扩展性的补充,

(如JSP中option标签的每一个value属性,如某个表'kind'之类字段的值解释,如一些配置属性),数据量不会很大,

基本上,数据是开发人员录入,项目不会对它有增、改、移除、擦除操作,只有查询操作,一个不是特别大的项目只需要一张字典表。

一般只有以下字段

key varchar(255) 主键,字典的索引

value varchar(255)  字典的值

group varchar(255) 用来表示key属于那一类,可以为空

sort int 用来表示key在自己所在group的顺序,可以为空

disable tinyint(1) 用来表示这条数据是否被失效,不能为空,默认0

6、信息表之间一对一的关系,设计成两张表的id字段一致

例如`person`表示例中,每个人都有“学生”和“子女”两个不同模块的属性,可以将它们分别抽取出来

CREATE TABLE `person` (
  `id` int(11) unsigned zerofill NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `sex` tinyint(1) DEFAULT NULL,
  `birthday` date DEFAULT NULL,
  `vip_flag` tinyint(1) DEFAULT NULL,
  `insert_time` datetime NOT NULL,
  `update_time` datetime DEFAULT NULL,
  `remove_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)  #与其他两张表的id形成逻辑外键
)
CREATE TABLE `student` (
  `id` int(11) unsigned zerofill NOT NULL AUTO_INCREMENT,
  `tuition` decimal(8,2) DEFAULT NULL,
  `payment_time` datetime DEFAULT NULL,
  `insert_time` datetime NOT NULL,
  `update_time` datetime DEFAULT NULL,
  `remove_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)  #与其他两张表的id形成逻辑外键
)
CREATE TABLE `child` (
  `id` int(11) unsigned zerofill NOT NULL AUTO_INCREMENT,
  `father_age` int(3) DEFAULT NULL,
  `father_job` int(3) DEFAULT NULL,  
  `mother_age` int(3) DEFAULT NULL,    
  `mother_job` int(3) DEFAULT NULL,  
  `insert_time` datetime NOT NULL,
  `update_time` datetime DEFAULT NULL,
  `remove_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)  #与其他两张表的id形成逻辑外键
)

7、信息表之间一对多的关系,在“多”的表中追加一个 `for_表名` 字段(如果其他字段中有单词for,有时候也会设计成`表名_id`)

例如,一家学校当前有多个学生,一个学生当前只能属于一个学校

CREATE TABLE `school` (
  `id` int(11) unsigned zerofill NOT NULL AUTO_INCREMENT,
  `address` varchar(200) DEFAULT NULL,
  `insert_time` datetime NOT NULL,
  `update_time` datetime DEFAULT NULL,
  `remove_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
)
CREATE TABLE `student` (
  `id` int(11) unsigned zerofill NOT NULL AUTO_INCREMENT,
  `for_school` int(11) NOT NULL DEFAULT '0',  #与school.id形成逻辑外键
  `name` varchar(20) DEFAULT NULL,
  `insert_time` datetime NOT NULL,
  `update_time` datetime DEFAULT NULL,
  `remove_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
)

8、关联表的命名,一般是两张“多”的表的表名用2链接

例如一个学生选择多门课程,一门课程供多名学生选择

CREATE TABLE `selectable_lesson` (
  `id` int(11) unsigned zerofill NOT NULL AUTO_INCREMENT,  
  #省略
  PRIMARY KEY (`id`)
)

CREATE TABLE `normal_student` (
  `id` int(11) unsigned zerofill NOT NULL AUTO_INCREMENT,  
  #省略
  PRIMARY KEY (`id`)
)

CREATE TABLE `normal_student2selectable_lesson` (  #意为normal student to selectable lesson
  `id` int(11) unsigned zerofill NOT NULL AUTO_INCREMENT,
  `for_normal_student` int(11) NOT NULL DEFAULT '0',  #与normal_student.id形成逻辑外键
  `for_selectable_lesson` int(11) NOT NULL DEFAULT '0',  #与selectable_lesson.id形成逻辑外键
  `score` int(3) DEFAULT NULL,
  `insert_time` datetime NOT NULL,
  `update_time` datetime DEFAULT NULL,
  `remove_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
)

9、信息表的中字段名中不出现表名,

如不采用user.username,而是采用user.name

11、信息表中涉及到“属于什么类型”、“是哪种分类”概念的字段,用`kind`表示,如user.kind

涉及到以上概念信息表,用诸如`food_type`来表示。

10、类型

字符串长度不变的用char(n),

字符串能大致确定范围的用varchar(n),

字符串范围超过65532的用text,

年龄之类的字段通常用int(n),

上限不定的个数类字段通常用int默认,即最长11,

更长时用bigint,

日期用date,

日期+时间用datetime,

时间用time,

区分类字段(如“性别”,“直辖市”等)用tiny(1)(并在Java代码中定义好规约),

是/否类字段用tiny(1),

价格类字段用decimal(8,2),-99,999,999.99 ~ 99,999,999.99,

电话/手机用varchar(20),

`id`用int,`insert_time`和`update_time`和`remove_time`用datetime

原文地址:https://www.cnblogs.com/deolin/p/7353339.html