MySQL建库建表笔记

本文系统梳理MySQL 8.0+环境下建库建表的关键实践:强调utf8mb4字符集的必要性(支持Emoji及完整Unicode);详解库表命名规范、数据类型选型原则(避免空间浪费);覆盖建表语法、约束体系(NOT NULL、DEFAULT、PRIMARY KEY、AUTO_INCREMENT、UNIQUE、FOREIGN KEY、CHECK)及其使用陷阱;说明存储引擎选择、字符集/校对规则查看方法,以及建表模板与安全操作要点。

MySQL 建库建表笔记

目录


一、建库:先把库跑起来

最常用的几条命令,先背下来:

-- 新建数据库
create database 数据库名;

-- 设为当前数据库
use 数据库名;

-- 查看当前使用的数据库(没有选中库时返回 NULL)
select database();

-- 显示所有数据库
show databases;

-- 删除数据库
drop database 数据库名;

几个我踩过的坑

1. database 是关键字,直接当库名会报错。

mysql> create database database;
ERROR 1064 (42000): You have an error in your SQL syntax; ...

用反引号包起来就没事了:

mysql> create database `database`;
Query OK, 1 row affected (0.01 sec)

反引号 ` 的作用就是避免和 SQL 语法冲突,顺便让代码更规范。
我的习惯是:保留字和特殊字符必须加,普通表名列名不用加,但为了统一风格我还是会加上。
另外别拿关键字当库名表名实在不优雅。

2. database 没有 s,但 show databases 有 s。

  • show databases —— 复数,看所有库
  • show create database xxx —— 单数,看建库语句

这两个最容易写混。

3. 库已存在会直接报错。

mysql> create database testdb;
ERROR 1007 (HY000): Can't create database 'testdb'; database exists

所以我写建库脚本时习惯带上 if not exists:create database if not exists 库名;

4. use 后面可以不加分号。

mysql> use testdb;
Database changed

5. 查建库语句用 \G 纵向显示,不然长语句会折行看不清楚。

mysql> show create database testdb\G
*************************** 1. row ***************************
       Database: testdb
Create Database: CREATE DATABASE `testdb` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */

这行可以拆成三段看:

片段含义
CREATE DATABASE \testdb``创建名为 testdb 的库
/*!40100 ... */注释形式的字符集设置:utf8mb4 + utf8mb4_0900_ai_ci(ai 不区分重音、ci 不区分大小写)
/*!80016 DEFAULT ENCRYPTION='N' */数据库未加密

系统自带的 4 个库

库名存什么
information_schema数据字典,库表列的元信息都在这(数据字典表)
performance_schema服务器性能参数
mysql用户权限、账号信息
sys5.7 新增,把前两个库以视图方式组合起来,方便人看

这四个别删。


二、字符集:utf8 还是 utf8mb4

MySQL 里字符集是两个概念,容易混:

  • Character Set(字符集):字符的编码规则,决定字符怎么存、占几个字节、支持哪些字符
  • Collation(字符序):排序和比较的规则集合,比如 A>B>a>b 还是 a>b>A>B

为什么必须用 utf8mb4

MySQL 5.7 默认字符集是 latin1,8.0 是 utf8mb4。

关键是 MySQL 里的 utf8 其实只是 utf8mb3 的别名,一个字符最多占 3 字节,只能编码到 Unicode 的 0xffff,也就是基本多文种平面(BMP)。

这就意味着:Emoji 表情、很多生僻字、任何新增的 Unicode 字符,MySQL 的 utf8 都存不进去,一存就报错。

utf8mb4 里的 mb4 = most bytes 4,是真正的 4 字节 UTF-8,完整支持 Unicode,而且是 utf8mb3 的超集,不用做额外转换。

我的结论:8.0 之后,库表和程序里一律 utf8mb4,别再写 utf8 了。

选用规则

  • 面向国外业务、多种语言 → utf8mb4
  • 只支持中文、不涉及国外业务 → 可以考虑 GBK 换性能(但 GBK 不是国际通用字符集)

常用查看命令

show character set;                    -- 所有支持的字符集
show character set like 'utf%';
show collation;                        -- 所有字符序
show variables like 'character_set%';  -- 当前字符集设置

字符序的命名规则是「字符集名 + 国家名(或 general)+ 后缀」:

后缀含义
ci大小写不敏感
cs大小写敏感
ai口音不敏感
as口音敏感
bin二进制比较
ks假名敏感

utf8mb4_0900_ai_ci 就是:utf8mb4 字符集 + Unicode 9.0 + 不区分口音 + 不区分大小写。

character_set% 的变量里,我重点关注两个:character_set_database(库默认字符集)和 character_set_server(服务器默认字符集)。


三、命名规范

通用规则

  • 用有意义的英文词,中间用下划线 _ 分隔
  • 只能由字母、数字、#、_、$ 组成,且必须以英文字母开头
  • 不要用 MySQL 保留字
  • 统一小写
  • 同一类对象里不能重名

生产环境约定

数据库:一般不超过 30 个字符,格式是「项目名 + 库含义简写」,比如 IM 项目的工作流库叫 im_flow,且用小写。

表:

类型命名规则示例
常规表t_ + 模块 + 表t_user_eduinfo
临时表temp_ + 模块 + 表 + 日期temp_user_eduinfo_20240520
备份表bak_ + 模块 + 表 + 日期bak_user_eduinfo_20231219

同模块的表尽量用相同前缀,表名尽量控制在 30 字符以内。


四、数据类型速查

整型

类型字节带符号范围无符号范围
tinyint1-128 ~ 1270 ~ 255
smallint2-32768 ~ 327670 ~ 65535
mediumint3-8388608 ~ 83886070 ~ 16777215
int4-2147483648 ~ 21474836470 ~ 4294967295
bigint8-9223372036854775808 ~ 92233720368547758070 ~ 18446744073709551616

选型心得:一定要给每列挑最合适的大小。
比如一个只有 0/1 两种值的枚举字段,用 TINYINT 就够,非要上 BIGINT 是纯浪费。100 万行数据就白白多占 700 万字节(约 6.7 MB),表一多就很可观。

另外 int(10) 里的 10 不是存储宽度,只是显示宽度,不够补 0,超了直接显示全部数字。
int 永远是 4 字节。MySQL 8.0.17 之后这个写法已经不推荐了,直接写 int 就行。

浮点与定点

类型字节说明
float4单精度浮点
double8双精度浮点
decimal-定点型,金额用它

可以写 float(M,D)、double(M,D) 来限制宽度和精度,不指定就按实际精度处理。

文本类型

类型说明
char(n)定长,0~255,不够用空格补,尾部空格会被去掉
varchar(n)变长,最多 255,超过自动转 TEXT
tinytext / text / mediumtext / longtext大文本,text 存 65535 字符
enum单选
set多选,最多 64 个列表项

char 适合长度固定的内容(手机号、身份证号),varchar 适合长度不固定的内容。

blob 和 text 都是存大数据的,区别是:blob 二进制形式存储,没有排序规则和字符集;text 字符形式存储,有排序规则和字符集。文章内容、评论这类用 text。

日期时间类型

类型字节格式
date3yyyy-MM-dd
time3HH:mm:ss
year1yyyy
datetime8yyyy-MM-dd HH:mm:ss
timestamp4yyyy-MM-dd HH:mm:ss,可当时间戳

五、建表

语法

create table [if not exists] 表名 (
    列名1 列类型1 [auto_increment] [default 默认值] [列约束] [comment '注释'],
    列名2 列类型2 [auto_increment] [default 默认值] [列约束] [comment '注释'],
    ...,
    [表约束]
) [engine=存储引擎] [default charset=字符集];

三个必须记住的点

  1. 表属于数据库,建表前必须先 use 到对应库,否则报 No database selected
  2. 表名不区分大小写,不能是 SQL 关键字(DROP、ALTER、INSERT 等)
  3. 必须指定每一列的名称和类型,多列用逗号隔开;表已存在会报错,用 if not exists 兜底

完整示例

mysql> create database mydb2_stuinfo;
mysql> use mydb2_stuinfo;
mysql> create table student1(
    ->   id   int unsigned,
    ->   name varchar(30),
    ->   sex  char(2),
    ->   age  int unsigned,
    ->   score float,
    ->   addr varchar(50)
    -> );
Query OK, 0 rows affected (0.02 sec)

查看表

show tables;                            -- 当前库的所有表
show tables from mydb3_employee;        -- 看别的库的表
show tables like 'stu%';                -- 通配符匹配

desc student1;                          -- = describe student1
show columns from student1;             -- 同上

show create table student1\G            -- 完整建表语句(\G 纵向显示)

desc 的字段含义:

字段含义
Field字段名
Type字段类型
Null是否允许为空
Key索引类型(PRI 主键 / UNI 唯一 / MUL 普通索引)
Default默认值
Extra额外信息,auto_increment 就在这里

show create table 的输出结尾会带存储引擎和字符集,比如:

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

顺便提一句,所有库表列的元信息都在系统库 information_schema 里,需要的时候可以查它。

删除表

drop table 表名;
drop table if exists 表名;   -- 推荐写法,先判断存在再删

六、修改表和删除表

-- 改表名(两种写法都行)
alter table student2 rename student3;
rename table student3 to teacher1;

-- 新增列
alter table 表名 add 列名 列类型;                    -- 加到最后
alter table 表名 add 列名 列类型 after 某列;        -- 加到指定列后面
alter table 表名 add 列名 列类型 first;              -- 加到第一列

-- 删除列
alter table 表名 drop 列名;

-- 修改列名
alter table 表名 change 旧列名 新列名 列类型;

-- 修改列类型
alter table 表名 modify 列名 新类型;

-- 修改列位置
alter table 表名 modify 列名 列类型 after 某列;
alter table 表名 modify 列名 列类型 first;

注意:没有 before 关键字,只有 after 和 first。
另外 modify 和 change 都会重写整列定义,原来的约束如果没写出来就丢了,动手前先 desc 看一眼。


七、复制表

-- 只复制表结构(字段、类型、索引、约束)
create table 新表名 like 源表;

-- 复制表结构 + 数据
create table 新表名 select * from 源表;

-- 只复制数据
insert into 新表名 select * from 源表;

做备份或者搭测试表的时候,这三条基本够用了。


八、约束

为什么需要约束

数据类型只能约束「是不是这个类型」,但约束不了「这个值合不合理」。比如身份证号字段,光是 varchar 类型显然不够,还得要求它唯一。

约束 = 表上强制执行的数据校验规则,本质是 MySQL 通过限制用户操作来维护数据安全和完整性。

四种完整性:

完整性含义例子
实体完整性不能有两条完全相同、无法区分的记录主键唯一
域完整性值必须在合理范围内年龄 0~120、性别 男/女
引用完整性引用的数据必须存在员工所在部门要在部门表里能找到
用户自定义完整性业务规则用户名唯一、密码不能为空

补充一点:约束不只出现在数据库层,业务代码里同样可以做校验,两边配合效果最好。

列级约束 vs 表级约束

-- 列级:定义列的同时写约束
create table 表名 (列名 类型 约束);

-- 表级:所有列定义完再写
create table 表名 (列信息, constraint 约束名 约束类型(列名));

约束名推荐格式:表名_列名_约束类型,比如 t_student_id_pk。

表级约束不能直接修改,只能先删掉再加新的。

1. 非空约束 NOT NULL

alter table 表名 modify 列名 类型 not null;
alter table 表名 modify 列名 类型 null;    -- 取消非空

实际开发尽量别留空值,空数据没法参与运算。
记牢:空字符串 '' 不等于 NULL,0 也不等于 NULL,所有类型的值都可以是 NULL。

2. 默认值约束 DEFAULT

alter table 表名 modify 列名 类型 default '值';

插入时不显式给这个字段,就用默认值。

坑:如果某字段同时设了 not null 和 default,那 not null 就没意义了 —— 不传值走默认值,not null 永远不会触发。

3. 列描述 COMMENT

alter table 表名 modify 列名 类型 comment '说明文字';

字段多了以后,注释就是救命的东西。

4. 主键约束 PRIMARY KEY

create table t1 (
    id   int unsigned primary key comment '学号',
    name varchar(20) not null,
    sex  char(2) default '男'
);

alter table 表名 add primary key (列名);    -- 表级主键
alter table 表名 drop primary key;          -- 删除

特点:

  • 主键字段不能为空、不能重复
  • 一张表最多一个主键
  • 通常用整型
  • 创建主键时会自动建立主键索引(按主键查效率更高);删掉主键约束,索引也跟着没了
  • 不要随意修改主键的值,它是记录的唯一标识,改了容易破坏数据完整性

联合主键:

create table t2 (
    id       int,
    hostname varchar(10),
    ip       varchar(20),
    port     int unsigned,
    primary key (ip, port)
);

5. 主键自增长 AUTO_INCREMENT

alter table 表名 modify 列名 类型 auto_increment;

规则:

  • 必须是整数类型
  • 必须建在有索引的列上(desc 里 Key 那栏要有值)
  • 一张表最多一个自增列
  • 默认从 1 开始递增
  • 可以手动插入值,只要不重复,之后新数据从最大值继续递增

6. 唯一性约束 UNIQUE

alter table 表名 add constraint 约束名 unique (列名);
alter table 表名 drop index 约束名;

和主键的区别,我这么理解:

  • 主键:标识唯一性,是「查数据用」的钥匙
  • 唯一键:保证业务字段不重复,是「防脏数据用」的

两者不是对立关系,是互补关系。一张表可以有一个主键 + 多个唯一键。

比如学生表:身份证号当主键,学号不能重复 —— 学号就设成唯一键。唯一键允许为 NULL(多个 NULL 不算重复)。

坑:唯一键冲突时,自增会出现跳号(比如 id 跳到 5 去了),这是正常现象,不是 bug。

7. 外键约束 FOREIGN KEY

-- 语法
foreign key (从表的字段) references 主表(主表的字段)

-- 建表时加
create table 子表 (
    ...,
    class_id int,
    foreign key (class_id) references 班级表(id)
);

-- 建表后加
alter table 从表 add constraint 约束名 foreign key (从表字段) references 主表(主表字段);
alter table 从表 drop foreign key 约束名;

几个概念:

  • 主表(父表):被引用的那张表
  • 从表(子表):引用别人的那张表
  • 外键约束定义在从表上,主表必须有主键或唯一键约束

要求:

  1. 主表必须已存在,或者就是当前正在建的表(自参照表)
  2. 外键列的个数要和主表主键的列数一致(联合主键时)
  3. 外键列的数据类型必须和主表主键对应列完全相同
  4. 外键列可以是 NULL,表示还没关联上(比如学生还没分班)

作用就是给表之间加了一层校验:往从表插数据时会检查这个值在主表存不存在;删主表数据时也会挡住还有子表引用的行。

8. 检查约束 CHECK(8.0.16+)

check (salary > 0 and salary < 10000)

alter table 表名 add constraint 约束名 check (条件);
alter table 表名 drop check 约束名;

报错长这样:

ERROR 3819 (HY000): Check constraint 't5_chk_1' is violated.

删除约束速查表

约束级别删除方式
NOT NULL仅列级alter table 表名 modify 列名 类型
DEFAULT仅列级alter table 表名 modify 列名 类型
AUTO_INCREMENT(不算约束,是列属性)仅列属性alter table 表名 modify 列名 类型
PRIMARY KEY列级 / 表级alter table 表名 drop primary key
UNIQUE列级 / 表级alter table 表名 drop index 约束名
FOREIGN KEY列级 / 表级alter table 表名 drop foreign key 约束名
CHECK列级 / 表级alter table 表名 drop check 约束名

九、存储引擎

存储引擎是 MySQL 底层的软件组织方式,MySQL 的核心特性之一就是插件式存储引擎 —— 用户可以根据应用需要选择怎么存、怎么建索引、要不要事务。

引擎特点适用场景
MyISAM5.5 之前的默认引擎,插入查询快,不支持事务、不支持外键以读写为主、很少更新、事务一致性要求不高
InnoDB8.0 默认引擎,支持 ACID 事务、行级锁、支持外键事务完整性要求高、并发一致性要求高
MEMORY数据全在内存,效率极高,但重启数据就没了,占用内存和数据量成正比临时数据
Archive插入极快,查询支持差存大量归档、历史记录
CSV纯文本文件,每行一条,不支持索引导出、导入中间态
BlackHole写进去的数据直接消失记录 binlog 做复制中继
FEDERATED把不同 MySQL 服务器逻辑上组成一个库分布式应用

对比几个关键维度:

功能MyISAMMEMORYInnoDB
支持事务否否是
支持外键否否是
支持行级锁否否是
存储限制256 TBRAM64 TB
空间使用率低N/A高

查看命令:

show engines\G;                            -- 支持哪些引擎
show variables like '%storage_engine%';     -- 当前默认引擎

建表时指定:

create table 表名 (...) engine=InnoDB;

不指定的话就走 my.cnf 里的 default-storage-engine=InnoDB。


十、完整建表语句模板

把这张表存下来,建表时照着抄:

create table 表名 (
    列名1  列类型  [auto_increment] [default 默认值] [列约束] [comment '注释'],
    列名2  列类型  [auto_increment] [default 默认值] [列约束] [comment '注释'],
    ...,
    [表约束]
) engine=InnoDB default charset=utf8mb4;

各部分说明:

部分含义
列类型该列数据的存储类型
auto_increment自动增长,只能用在数值类型的列
default该列的默认值
列约束对列的一些限制(not null、primary key、unique、check、comment)
表约束主键、外键等跨列的约束
engine存储引擎
charset表的字符集

我实际写的一个例子:

create table pro_tb(
    pro_num   int unique,                  -- 产品编号:唯一键
    pro_name  varchar(30) not null,        -- 产品名:非空
    pro_price float(5,2) not null,         -- 单价:非空
    pro_spe   varchar(8) default '件'      -- 规格:默认值
) engine=InnoDB default charset=utf8mb4;

总结

这章内容不少,但拎出来也就几条主线,整理成我自己的 checklist:

  1. 建库建表前先 use,不然直接报 No database selected
  2. 字符集统一 utf8mb4,因为 MySQL 的 utf8 存不了 Emoji
  3. 关键字和特殊字符用反引号包起来,database 就是个现成的例子
  4. 数据类型按需选,能用 tinyint 就别上 bigint,别用 float 存金额
  5. 主键 + 唯一键 + 非空 是每张表的基本盘
  6. 外键先想清楚再上,它会连带影响插入和删除的行为
  7. 命名跟着生产规范走,t_ / temp_ / bak_ 三种前缀先记住
  8. 建表语句模板存一份,比每次现想强

。


环境:MySQL 8.0.37(CentOS/RHEL)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值