1.名字
1.1 见名知意
反例:
用户名称字段定义成:yong_hu_ming、用户_name、name、user_name_123456789
你看了可能会一脸懵逼,这是什么骚操作?
正例:
用户名称字段定义成:user_name
1.2 大小写
字段名:PRODUCT_NAME、PRODUCT_name
字段名:product_name
1.3 分隔符
字段名:productname、productName、product name、product@name
强烈建议大家在单词间用_分隔。
1.4 表名
这样就不会出现同名表的情况。
1.5 字段名称
其实还有很多公共字段,在不同的表之间,可以使用全局统一的命名规则,定义成相同的名称,以便于大家好理解。
1.6 索引名
2.字段类型
以下原则可以参考一下:
尽可能选择占用存储空间小的字段类型,在满足正常业务需求的情况下,从小到大,往上选。 如果字符串长度固定,或者差别不大,可以选择char类型。如果字符串长度差别较大,可以选择 varchar 类型。 是否字段,可以选择 bit 类型。 枚举字段,可以选择 tinyint 类型。 主键字段,可以选择 bigint 类型。 金额字段,可以选择 decimal 类型。 时间字段,可以选择 timestamp 或 datetime 类型。
3.字段长度
前面我们已经定义好了字段名称,选择了合适的字段类型,接下来,需要重点关注的是字段长度了。
比如:varchar(20),biginit(20) 等。
那么问题来了,varchar 代表的是字节长度,还是字符长度呢?
答:在 MySQL 中除了 varchar 和 char 是代表字符长度之外,其余的类型都是代表字节长度。
biginit(n) 这个 n 表示什么意思呢?
假如我们定义的字段类型和长度是:bigint(4),bigint 实际长度是 8 个字节。
现在有个数据 a=1,a 显示 4 个字节,所以在不满 4 个字节时前面填充 0(前提是该字段设置了 zerofill 属性),比如:0001。
当满了 4 个字节时,比如现在数据是 a=123456,它会按照实际的长度显示,比如:123456。
4.字段个数
5. 主键
这样,用户扩展表的主键,可以直接保存用户表的主键。
6.存储引擎
7. NOT NULL
为什么呢?
我们主要以 innodb 存储引擎为例,myisam 存储引擎没啥好说的。
主要有以下原因:
在 innodb 中,需要额外的空间存储 null 值,需要占用更多的空间。 null 值可能会导致索引失效。 null 值只能用 is null 或者 is not null 判断,用 = 号判断永远返回 false。
因此,建议我们在定义字段时,能定义成 NOT NULL,就定义成 NOT NULL。
但如果某个字段直接定义成 NOT NULL,万一有些地方忘了给该字段写值,就会 insert 不了数据。
这也算合理的情况。
但有一种情况是,系统有新功能上线,新增了字段。上线时一般会先执行 SQL 脚本,再部署代码。
由于老代码中,不会给新字段赋值,则 insert 数据时,也会报错。
由此,非常有必要给 NOT NULL 的字段设置默认值,特别是后面新增的字段。
例如:
alter table product_sku add column brand_id int(10) not null default 0;
8. 外键
create table class (
id int(10) primary key auto_increment,
cname varchar(15)
);
有个班级表 class。
然后有个 student 表:
create table student(
id int(10) primary key auto_increment,
name varchar(15) not null,
gender varchar(10) not null,
cid int,
foreign key(cid) references class(id)
);
其中 student 表中的 cid 字段,保存的 class 表的 id,这时通过 foreign key 增加了一个外键。
这时,如果你直接通过 student 表的 id 删除数据,会报异常:
a foreign key constraint fails
必须要先删除 class 表对于的 cid 那条数据,再删除 student 表的数据才行,这样能够保证数据的一致性和完整性。
顺便说一句:只有存储引擎是 innodb 时,才能使用外键。
如果只有两张表的关联还好,但如果有十几张表都建了外键关联,每删除一次主表,都需要同步删除十几张子表,很显然性能会非常差。
因此,互联网系统中,一般建议不使用外键。因为这类系统更多的是为了性能考虑,宁可牺牲一点数据一致性和完整性。
9. 索引
create table product_sku(
id int(10) primary key auto_increment,
spu_id int(10) not null,
brand_id int(10) not null,
name varchar(15) not null
);
在创建商品表时,使用 spu_id(商品组表)和 brand_id(品牌表)的 id。
像这类保存其他表 id 的情况,可以增加普通索引:
create table product_sku (
id int(10) primary key auto_increment,
spu_id int(10) not null,
brand_id int(10) not null,
name varchar(15) not null,
KEY `ix_spu_id` (`spu_id`) USING BTREE,
KEY `ix_brand_id` (`brand_id`) USING BTREE
);
后面查表的时候,效率更高。
但索引字段也不能建的太多,可能会影响保存数据的效率,因为索引需要额外的存储空间。
建议单表的索引个数不要超过:5 个。
如果在建表时,发现索引个数超过 5 个了,可以删除部分普通索引,改成联合索引。
10. 时间字段
但如果哪天我们要通过时间范围查询数据,效率会非常低,因为这种情况没法走索引。
date 类型主要是为了保存日期,比如:2020-08-20,不适合保存日期和时间,比如:2020-08-20 12:12:20。
而 datetime 和 timestamp 类型更适合我们保存日期和时间。
timestamp:用 4 个字节来保存数据,它的取值范围为 1970-01-01 00:00:01 UTC ~ 2038-01-19 03:14:07。此外,它还跟时区有关。 datetime:用 8 个字节来保存数据,它的取值范围为 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59。它跟时区无关。
温馨提醒一下,在给时间字段设置默认值是,建议不要设置成:0000-00-00 00:00:00,不然查询表时可能会因为转换不了,而直接报错。
11.金额字段
假如我们定义的金额类型是这样的:decimal(10,2),则表示整数长度是8位,并且保留 2 位小数。
12. json 字段
MySQL 还支持按字段名称或者字段值,查询 json 中的数据。
13.唯一索引
14. 字符集
MySQL 中支持的字符集有很多,常用的有:latin1、utf-8、utf8mb4、GBK 等。
这 4 种字符集情况如下:
latin1 容易出现乱码问题,在实际项目中使用比较少。
而 GBK 支持中文,但不支持国际通用字符,在实际项目中使用也不多。
从目前来看,MySQL 的字符集使用最多的还是:utf-8 和 utf8mb4。
其中 utf-8 占用 3 个字节,比 utf8mb4 的 4 个字节,占用更小的存储空间。
但 utf-8 有个问题:即无法存储 emoji 表情,因为 emoji 表情一般需要 4 个字节。
由此,使用 utf-8 字符集,保存 emoji 表情时,数据库会直接报错。
15. 排序规则
CREATE TABLE `order` (
`id` bigint NOT NULL AUTO_INCREMENT,
`code` varchar(20) COLLATE utf8mb4_bin NOT NULL,
`name` varchar(30) COLLATE utf8mb4_bin NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `un_code` (`code`),
KEY `un_code_name` (`code`,`name`) USING BTREE,
KEY `idx_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
select * from order where name='yoyo';
如果字符排序规则是 utf8mb4_general_ci,则可以查出大写的 YOYO 的那条数据。
如果字符排序规则是 utf8mb4_bin,则查不出来。
16.大字段
一个合同可能会占几 Mb。
在 MySQL 中保存这种数据,从系统设计的角度来说,本身就不太合理。
17. 冗余字段
我们可以在订单表中,可以再加一个 userName 字段,在系统创建订单时,将 userId 和 userName 同时写值。
当然订单表中历史数据的 userName 是空的,可以刷一下历史数据。
这样调整之后,后面只需要查询订单表,即可查询出我们所需要的数据。
18. 注释
CREATE TABLE `sys_dept` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
`name` varchar(30) NOT NULL COMMENT '名称',
`pid` bigint NOT NULL COMMENT '上级部门',
`valid_status` tinyint(1) NOT NULL DEFAULT 1 COMMENT '有效状态 1:有效 0:无效',
`create_user_id` bigint NOT NULL COMMENT '创建人ID',
`create_user_name` varchar(30) NOT NULL COMMENT '创建人名称',
`create_time` datetime(3) DEFAULT NULL COMMENT '创建日期',
`update_user_id` bigint DEFAULT NULL COMMENT '修改人ID',
`update_user_name` varchar(30) DEFAULT NULL COMMENT '修改人名称',
`update_time` datetime(3) DEFAULT NULL COMMENT '修改时间',
`is_del` tinyint(1) DEFAULT '0' COMMENT '是否删除 1:已删除 0:未删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `index_pid` (`pid`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='部门';
表和字段的注释,都列举的非常详细。
特别是有些状态类型的字段,比如:valid_status 字段,该字段表示有效状态, 1:有效 0:无效。
让人可以一目了然,表和字段是干什么用的,字段的值可能有哪些。
最怕的情况是,你在表中创建了很多 status 字段,每个字段都有1、2、3、4、5、6、7、8、9等多个值。
没有写什么注释。
谁都不知道 1 代表什么含义,2 代表什么含义,3 代表什么含义。
可能刚开始你还记得。
但系统上线使用一年半载之后,可能连你自己也忘记了这些 status 字段,每个值的具体含义了,埋下了一个巨坑。
由此,我们在做表设计时,一定要写好相关的注释,并且经常需要更新这些注释。