数据表的创建与完整性约束
6.8 数据表的创建
6.8.1 选择数据库
在创建表之前,必须先选择数据库:
USE 学生管理;
6.8.2 数据类型
整数类型
| 类型 | 字节 | 范围(有符号) | 范围(无符号) |
|---|---|---|---|
| TINYINT | 1 | -128 ~ 127 | 0 ~ 255 |
| SMALLINT | 2 | -32768 ~ 32767 | 0 ~ 65535 |
| MEDIUMINT | 3 | -8388608 ~ 8388607 | 0 ~ 16777215 |
| INT | 4 | -2^31 ~ 2^31-1 | 0 ~ 2^32-1 |
| BIGINT | 8 | -2^63 ~ 2^63-1 | 0 ~ 2^64-1 |
-- 无符号整数示例
年龄 TINYINT UNSIGNED -- 0-255,适合存储年龄
浮点数类型
| 类型 | 说明 |
|---|---|
| FLOAT | 单精度浮点数 |
| DOUBLE | 双精度浮点数 |
| DECIMAL(M,D) | 精确小数,M是总位数,D是小数位数 |
-- 货币推荐使用 DECIMAL
价格 DECIMAL(10,2) -- 最大99999999.99
字符串类型
| 类型 | 说明 | 最大长度 |
|---|---|---|
| CHAR(n) | 定长字符串 | 255字符 |
| VARCHAR(n) | 变长字符串 | 65535字节 |
| TEXT | 长文本 | 65535字节 |
| MEDIUMTEXT | 中等长度文本 | 16MB |
| LONGTEXT | 超长文本 | 4GB |
flowchart LR
subgraph "CHAR vs VARCHAR"
C["CHAR(10) 存储 'abc'<br>→ 占用10个字符空间(补空格)"]
V["VARCHAR(10) 存储 'abc'<br>→ 占用3个字符 + 1字节长度标识"]
end
| 类型 | 适用场景 |
|---|---|
| CHAR | 固定长度:身份证号、手机号、邮编 |
| VARCHAR | 可变长度:姓名、地址、备注 |
日期时间类型
| 类型 | 格式 | 范围 |
|---|---|---|
| DATE | YYYY-MM-DD | 1000-01-01 ~ 9999-12-31 |
| TIME | HH:MM:SS | -838:59:59 ~ 838:59:59 |
| DATETIME | YYYY-MM-DD HH:MM:SS | 1000年 ~ 9999年 |
| TIMESTAMP | YYYY-MM-DD HH:MM:SS | 1970年 ~ 2038年 |
| YEAR | YYYY | 1901 ~ 2155 |
-- TIMESTAMP 会自动转换时区,DATETIME 不会
创建时间 TIMESTAMP DEFAULT CURRENT_TIMESTAMP
其他类型
| 类型 | 说明 |
|---|---|
| BLOB | 二进制大对象 |
| ENUM | 枚举类型 |
| SET | 集合类型 |
| JSON | JSON格式数据(MySQL 5.7+) |
-- ENUM 示例
性别 ENUM('男', '女')
-- JSON 示例
配置信息 JSON
6.8.3 创建表的基本语法
USE 学生管理;
CREATE TABLE 学生
(
学号 CHAR(10),
姓名 VARCHAR(20),
性别 CHAR(2),
年龄 INT,
入学日期 DATE
);
6.8.4 创建表的完整示例
USE 学生管理;
CREATE TABLE 学生
(
学号 CHAR(10) NOT NULL,
姓名 VARCHAR(20) NOT NULL,
性别 CHAR(2),
出生日期 DATE,
班级 VARCHAR(30),
联系电话 CHAR(11),
备注 TEXT
);
查看表结构:
DESC 学生;
-- 或者
SHOW CREATE TABLE 学生;
6.8.5 计算列(生成列)
💡 MySQL 5.7+ 支持生成列
CREATE TABLE 商品
(
商品编号 CHAR(10),
商品名称 VARCHAR(50),
单价 DECIMAL(10,2),
数量 INT,
总价 DECIMAL(12,2) AS (单价 * 数量) STORED
);
| 生成列类型 | 说明 |
|---|---|
| VIRTUAL | 虚拟列,查询时计算,不占存储空间 |
| STORED | 存储列,插入/更新时计算,占用存储空间 |
-- 字符串连接示例
CREATE TABLE 员工
(
姓 VARCHAR(10),
名 VARCHAR(10),
全名 VARCHAR(20) AS (CONCAT(姓, 名)) VIRTUAL
);
6.9 完整性约束
6.9.1 空值约束(NULL / NOT NULL)
CREATE TABLE 教师
(
教师编号 CHAR(10) NOT NULL,
姓名 VARCHAR(20) NOT NULL,
性别 CHAR(2), -- 默认允许NULL
联系电话 CHAR(11) NULL -- 显式允许NULL
);
测试:
-- 成功
INSERT INTO 教师(教师编号, 姓名) VALUES ('T001', '张老师');
-- 失败:Column '姓名' cannot be null
INSERT INTO 教师(教师编号) VALUES ('T002');
6.9.2 主键约束(PRIMARY KEY)
单列主键
-- 写法1:列级约束
CREATE TABLE 学生
(
学号 CHAR(10) PRIMARY KEY,
姓名 VARCHAR(20) NOT NULL
);
-- 写法2:表级约束
CREATE TABLE 学生
(
学号 CHAR(10),
姓名 VARCHAR(20) NOT NULL,
PRIMARY KEY (学号)
);
-- 写法3:命名约束
CREATE TABLE 学生
(
学号 CHAR(10),
姓名 VARCHAR(20) NOT NULL,
CONSTRAINT PK_学生 PRIMARY KEY (学号)
);
复合主键(联合主键)
CREATE TABLE 选课成绩
(
学号 CHAR(10),
课程编号 CHAR(8),
成绩 DECIMAL(5,2),
CONSTRAINT PK_选课成绩 PRIMARY KEY (学号, 课程编号)
);
复合主键说明:
| 学号 | 课程编号 | 成绩 | 是否允许 |
|---|---|---|---|
| S001 | C001 | 85 | ✓ |
| S001 | C002 | 90 | ✓ 学号相同,课程不同,允许 |
| S002 | C001 | 88 | ✓ 课程相同,学号不同,允许 |
| S001 | C001 | 92 | ✗ 两个都相同,不允许! |
自增主键
CREATE TABLE 订单
(
订单ID INT PRIMARY KEY AUTO_INCREMENT,
订单日期 DATE,
客户名称 VARCHAR(50)
);
-- 插入时不需要指定订单ID
INSERT INTO 订单(订单日期, 客户名称) VALUES ('2024-01-15', '张三');
INSERT INTO 订单(订单日期, 客户名称) VALUES ('2024-01-16', '李四');
💡 AUTO_INCREMENT 特点:
- 每个表只能有一个自增列
- 自增列必须是主键或唯一键
- 默认从1开始,每次增加1
- 可以手动指定起始值:
AUTO_INCREMENT = 1000
6.9.3 外键约束(FOREIGN KEY)
创建主表和从表
-- 步骤1:创建主表(被引用的表)
CREATE TABLE 学院
(
学院编号 CHAR(2) PRIMARY KEY,
学院名称 VARCHAR(30) NOT NULL
);
-- 步骤2:创建从表(引用外键的表)
CREATE TABLE 教师
(
教师编号 CHAR(10) PRIMARY KEY,
姓名 VARCHAR(20) NOT NULL,
学院编号 CHAR(2),
CONSTRAINT FK_教师_学院
FOREIGN KEY (学院编号) REFERENCES 学院(学院编号)
);
外键约束关系图:
外键约束的作用
-- 在主表插入数据
INSERT INTO 学院 VALUES ('01', '计算机学院');
INSERT INTO 学院 VALUES ('02', '数学学院');
INSERT INTO 学院 VALUES ('03', '外语学院');
-- 在从表插入数据
INSERT INTO 教师 VALUES ('T001', '张老师', '01'); -- ✓ 成功
INSERT INTO 教师 VALUES ('T002', '李老师', '02'); -- ✓ 成功
INSERT INTO 教师 VALUES ('T003', '王老师', '04'); -- ✗ 失败!学院04不存在
级联操作
CREATE TABLE 教师
(
教师编号 CHAR(10) PRIMARY KEY,
姓名 VARCHAR(20) NOT NULL,
学院编号 CHAR(2),
CONSTRAINT FK_教师_学院
FOREIGN KEY (学院编号) REFERENCES 学院(学院编号)
ON UPDATE CASCADE -- 级联更新
ON DELETE CASCADE -- 级联删除
);
级联操作选项:
| 选项 | 说明 |
|---|---|
| CASCADE | 级联操作(删除/更新主表时,从表跟着变) |
| SET NULL | 设为NULL(主表删除/更新时,从表外键设为NULL) |
| RESTRICT | 限制(默认,主表有关联数据时不能删除/更新) |
| NO ACTION | 同 RESTRICT |
| SET DEFAULT | 设为默认值(InnoDB不支持) |
级联操作测试
-- 创建测试表
CREATE TABLE 部门
(
部门编号 CHAR(2) PRIMARY KEY,
部门名称 VARCHAR(30)
);
CREATE TABLE 员工
(
员工编号 CHAR(10) PRIMARY KEY,
姓名 VARCHAR(20),
部门编号 CHAR(2),
FOREIGN KEY (部门编号) REFERENCES 部门(部门编号)
ON UPDATE CASCADE
ON DELETE CASCADE
);
-- 插入测试数据
INSERT INTO 部门 VALUES ('01', '技术部');
INSERT INTO 员工 VALUES ('E001', '张三', '01');
-- 测试级联更新
UPDATE 部门 SET 部门编号 = '11' WHERE 部门编号 = '01';
-- 员工表的部门编号也自动变成 '11'
-- 测试级联删除
DELETE FROM 部门 WHERE 部门编号 = '11';
-- 员工表中部门编号为 '11' 的记录也被删除
6.9.4 唯一性约束(UNIQUE)
-- 单列唯一约束
CREATE TABLE 学生
(
学号 CHAR(10) PRIMARY KEY,
身份证号 CHAR(18) UNIQUE,
邮箱 VARCHAR(50) UNIQUE,
姓名 VARCHAR(20)
);
-- 复合唯一约束
CREATE TABLE 学生
(
学号 CHAR(10) PRIMARY KEY,
班级 VARCHAR(20),
姓名 VARCHAR(20),
CONSTRAINT UK_班级姓名 UNIQUE (班级, 姓名)
);
PRIMARY KEY vs UNIQUE:
| 对比项 | PRIMARY KEY | UNIQUE |
|---|---|---|
| 允许NULL | ✗ 不允许 | ✓ 允许(但只能有一个) |
| 数量限制 | 每表只能有一个 | 可以有多个 |
| 自动创建索引 | 聚簇索引 | 非聚簇索引 |
6.9.5 检查约束(CHECK)
⚠️ 注意:MySQL 8.0.16 之前版本不支持 CHECK 约束(会解析但不生效)
-- 性别检查
CREATE TABLE 教师
(
教师编号 CHAR(10) PRIMARY KEY,
姓名 VARCHAR(20) NOT NULL,
性别 CHAR(2) CHECK (性别 IN ('男', '女')),
年龄 INT CHECK (年龄 >= 18 AND 年龄 <= 65),
工资 DECIMAL(10,2) CHECK (工资 > 0)
);
常用检查约束示例
-- 成绩范围检查
CREATE TABLE 成绩
(
学号 CHAR(10),
课程编号 CHAR(8),
成绩 DECIMAL(5,2) CHECK (成绩 BETWEEN 0 AND 100),
PRIMARY KEY (学号, 课程编号)
);
-- 手机号格式检查
CREATE TABLE 用户
(
用户ID INT PRIMARY KEY AUTO_INCREMENT,
手机号 CHAR(11) CHECK (手机号 REGEXP '^1[3-9][0-9]{9}$'),
邮箱 VARCHAR(50) CHECK (邮箱 LIKE '%@%.%')
);
-- 日期逻辑检查
CREATE TABLE 项目
(
项目编号 CHAR(10) PRIMARY KEY,
开始日期 DATE,
结束日期 DATE,
CHECK (结束日期 > 开始日期)
);
MySQL 8.0 之前的替代方案
-- 使用 ENUM 替代简单的 CHECK
CREATE TABLE 教师
(
教师编号 CHAR(10) PRIMARY KEY,
姓名 VARCHAR(20) NOT NULL,
性别 ENUM('男', '女') -- 只能是 '男' 或 '女'
);
-- 使用触发器替代复杂的 CHECK
DELIMITER //
CREATE TRIGGER check_age_before_insert
BEFORE INSERT ON 教师
FOR EACH ROW
BEGIN
IF NEW.年龄 < 18 OR NEW.年龄 > 65 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '年龄必须在18-65之间';
END IF;
END //
DELIMITER ;
6.9.6 默认约束(DEFAULT)
CREATE TABLE 课程
(
课程编号 CHAR(8) PRIMARY KEY,
课程名称 VARCHAR(50) NOT NULL,
学分 DECIMAL(3,1) DEFAULT 2.0,
课程性质 VARCHAR(10) DEFAULT '必修',
创建时间 DATETIME DEFAULT CURRENT_TIMESTAMP
);
常用默认值
CREATE TABLE 订单
(
订单ID INT PRIMARY KEY AUTO_INCREMENT,
订单状态 VARCHAR(10) DEFAULT '待处理',
数量 INT DEFAULT 1,
折扣 DECIMAL(3,2) DEFAULT 1.00,
创建时间 TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
更新时间 TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
);
常用默认值函数:
| 函数 | 说明 |
|---|---|
CURRENT_TIMESTAMP |
当前日期时间 |
CURRENT_DATE |
当前日期 |
CURRENT_TIME |
当前时间 |
UUID() |
生成UUID(需要触发器) |
ON UPDATE CURRENT_TIMESTAMP |
更新时自动更新时间戳 |
⬅️ MySQL 数据库管理 🏠 00-数据库 ➡️ 数据表的修改与删除
💬 评论