---
title: "07-数据表的创建与完整性约束"
created: 2026-01-07
tags:
- 项目筑基
---
# 数据表的创建与完整性约束
### **6.8 数据表的创建**
#### **6.8.1 选择数据库**
在创建表之前,必须先选择数据库:
```sql
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 |
```sql
-- 无符号整数示例
年龄 TINYINT UNSIGNED -- 0-255,适合存储年龄
```
##### **浮点数类型**
| **类型** | **说明** |
| --- | --- |
| **FLOAT** | 单精度浮点数 |
| **DOUBLE** | 双精度浮点数 |
| **DECIMAL(M,D)** | 精确小数,M是总位数,D是小数位数 |
```sql
-- 货币推荐使用 DECIMAL
价格 DECIMAL(10,2) -- 最大99999999.99
```
##### **字符串类型**
| **类型** | **说明** | **最大长度** |
| --- | --- | --- |
| **CHAR(n)** | 定长字符串 | 255字符 |
| **VARCHAR(n)** | 变长字符串 | 65535字节 |
| **TEXT** | 长文本 | 65535字节 |
| **MEDIUMTEXT** | 中等长度文本 | 16MB |
| **LONGTEXT** | 超长文本 | 4GB |
```mermaid
flowchart LR
subgraph "CHAR vs VARCHAR"
C["CHAR(10) 存储 'abc'
→ 占用10个字符空间(补空格)"]
V["VARCHAR(10) 存储 'abc'
→ 占用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 |
```sql
-- TIMESTAMP 会自动转换时区,DATETIME 不会
创建时间 TIMESTAMP DEFAULT CURRENT_TIMESTAMP
```
##### **其他类型**
| **类型** | **说明** |
| --- | --- |
| **BLOB** | 二进制大对象 |
| **ENUM** | 枚举类型 |
| **SET** | 集合类型 |
| **JSON** | JSON格式数据(MySQL 5.7+) |
```sql
-- ENUM 示例
性别 ENUM('男', '女')
-- JSON 示例
配置信息 JSON
```
#### **6.8.3 创建表的基本语法**
```sql
USE 学生管理;
CREATE TABLE 学生
(
学号 CHAR(10),
姓名 VARCHAR(20),
性别 CHAR(2),
年龄 INT,
入学日期 DATE
);
```
#### **6.8.4 创建表的完整示例**
```sql
USE 学生管理;
CREATE TABLE 学生
(
学号 CHAR(10) NOT NULL,
姓名 VARCHAR(20) NOT NULL,
性别 CHAR(2),
出生日期 DATE,
班级 VARCHAR(30),
联系电话 CHAR(11),
备注 TEXT
);
```
**查看表结构:**
```sql
DESC 学生;
-- 或者
SHOW CREATE TABLE 学生;
```
![[image-8b517709.png]]
#### **6.8.5 计算列(生成列)**
> 💡 MySQL 5.7+ 支持生成列
```sql
CREATE TABLE 商品
(
商品编号 CHAR(10),
商品名称 VARCHAR(50),
单价 DECIMAL(10,2),
数量 INT,
总价 DECIMAL(12,2) AS (单价 * 数量) STORED
);
```
| **生成列类型** | **说明** |
| --- | --- |
| **VIRTUAL** | 虚拟列,查询时计算,不占存储空间 |
| **STORED** | 存储列,插入/更新时计算,占用存储空间 |
```sql
-- 字符串连接示例
CREATE TABLE 员工
(
姓 VARCHAR(10),
名 VARCHAR(10),
全名 VARCHAR(20) AS (CONCAT(姓, 名)) VIRTUAL
);
```
### **6.9 完整性约束**
![[image-04cd276c.png]]
#### **6.9.1 空值约束(NULL / NOT NULL)**
```sql
CREATE TABLE 教师
(
教师编号 CHAR(10) NOT NULL,
姓名 VARCHAR(20) NOT NULL,
性别 CHAR(2), -- 默认允许NULL
联系电话 CHAR(11) NULL -- 显式允许NULL
);
```
**测试:**
```sql
-- 成功
INSERT INTO 教师(教师编号, 姓名) VALUES ('T001', '张老师');
-- 失败:Column '姓名' cannot be null
INSERT INTO 教师(教师编号) VALUES ('T002');
```
![[image-417e415a.png]]
#### **6.9.2 主键约束(PRIMARY KEY)**
##### **单列主键**
```sql
-- 写法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 (学号)
);
```
##### **复合主键(联合主键)**
```sql
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 | ✗ 两个都相同,不允许! |
##### **自增主键**
```sql
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)**
##### **创建主表和从表**
```sql
-- 步骤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 学院(学院编号)
);
```
**外键约束关系图:**
![[image-d2e83e6f.png]]
##### **外键约束的作用**
```sql
-- 在主表插入数据
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不存在
```
![[image-d1cac2fc.png]]
##### **级联操作**
```sql
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不支持) |
###### **级联操作测试**
```sql
-- 创建测试表
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)**
```sql
-- 单列唯一约束
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 约束(会解析但不生效)
```sql
-- 性别检查
CREATE TABLE 教师
(
教师编号 CHAR(10) PRIMARY KEY,
姓名 VARCHAR(20) NOT NULL,
性别 CHAR(2) CHECK (性别 IN ('男', '女')),
年龄 INT CHECK (年龄 >= 18 AND 年龄 <= 65),
工资 DECIMAL(10,2) CHECK (工资 > 0)
);
```
##### **常用检查约束示例**
```sql
-- 成绩范围检查
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 之前的替代方案**
```sql
-- 使用 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)**
```sql
CREATE TABLE 课程
(
课程编号 CHAR(8) PRIMARY KEY,
课程名称 VARCHAR(50) NOT NULL,
学分 DECIMAL(3,1) DEFAULT 2.0,
课程性质 VARCHAR(10) DEFAULT '必修',
创建时间 DATETIME DEFAULT CURRENT_TIMESTAMP
);
```
##### **常用默认值**
```sql
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` | 更新时自动更新时间戳 |
---
⬅️ [[06-MySQL 数据库管理|MySQL 数据库管理]] 🏠 [[00-数据库|00-数据库]] ➡️ [[08-数据表的修改与删除|数据表的修改与删除]]