一、主键设计基础

1.1 概念定义

主键是用于唯一标识表中每一行记录的特殊字段。其核心特性包括:

  • 唯一性:表中每个主键值必须不同
  • 非空性:主键字段不能为NULL
  • 稳定性:主键值一旦确定不应频繁变更

在MySQL中,主键通过PRIMARY KEY语法定义,可以是单字段或组合字段。主键会自动创建聚集索引,直接影响数据存储方式。

1.2 创建示例

学生课程关系模型

1
2
3
4
5
6
7
8
9
10
11
12
## 学生表
| 字段名 | 类型 | 说明 |
|--------|------|------|
| id | int | 主键 |
| name | varchar(50) | 学生姓名 |

## 课程表
| 字段名 | 类型 | 说明 |
|--------|------|------|
| id | int | 主键 |
| title | varchar(100) | 课程名称 |
| student_id | int | 外键,关联学生表id |

1.3 验证示例

学生课程验证

1
2
3
4
5
6
7
8
9
INSERT INTO students (id, name) VALUES 
(1, '张三'),
(2, '李四'),
(3, '王五');

INSERT INTO courses (id, title, student_id) VALUES
(1, '数学', 1),
(2, '语文', 1),
(3, '英语', 2);

查询示例

1
2
3
4
5
6
7
8
SELECT students.name, courses.title 
FROM students
JOIN courses ON students.id = courses.student_id
WHERE students.id = 1;

执行结果:
张三 | 数学
张三 | 语文

1.4 注意事项

  • 主键选择原则:优先使用自增整数,其次是UUID,最后考虑自然键
  • 复合主键限制:字段数量不宜过多,最多16个
  • 名称规范:建议使用idpk_字段名命名主键字段
  • 性能影响:主键字段应尽量选择长度短的类型(如使用INT而非VARCHAR)

二、外键约束机制

2.1 概念定义

外键是用于建立和加强两个表数据关联性的字段。其核心作用包括:

  • 数据一致性:确保引用完整性
  • 关系维护:建立表与表之间的逻辑链接
  • 约束执行:支持ON DELETE和ON UPDATE行为选项

外键通过FOREIGN KEY语法定义,需明确指定关联的主键字段,MySQL 8.0支持REFERENCES语法中的级联操作。

2.2 创建示例

订单用户验证

1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE users (
user_id INT PRIMARY KEY,
username VARCHAR(50)
);

CREATE TABLE orders (
order_id INT PRIMARY KEY,
total_amount DECIMAL(10,2),
user_id INT,
FOREIGN KEY (user_id) REFERENCES users(user_id)
);

学生课程验证

1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE students (
id INT PRIMARY KEY,
name VARCHAR(50)
);

CREATE TABLE courses (
id INT PRIMARY KEY,
title VARCHAR(100),
student_id INT,
FOREIGN KEY (student_id) REFERENCES students(id)
);

2.3 行为选项示例

ON DELETE行为

1
2
3
4
5
6
ALTER TABLE courses 
ADD FOREIGN KEY (student_id)
REFERENCES students(id)
ON DELETE CASCADE ON UPDATE RESTRICT;

当删除学生表记录时,会自动删除关联的课程记录

ON UPDATE行为

1
2
3
4
5
6
ALTER TABLE courses 
ADD FOREIGN KEY (student_id)
REFERENCES students(id)
ON DELETE SET NULL ON UPDATE CASCADE;

当更新学生表主键时,会同步更新课程表外键值

2.4 注意事项

  • 约束条件:外键字段类型和长度必须与主键字段完全匹配
  • 命名规范:建议使用fk_字段名命名外键字段
  • 行为限制:MySQL 8.0支持的选项包括RESTRICT、CASCADE、SET NULL和SET DEFAULT
  • 索引要求:外键字段会自动创建索引,但如果已存在索引可省略

三、跨表关联实践

3.1 一对一关联

场景描述:每个学生对应一个唯一课程记录

1
2
3
4
5
6
7
8
9
10
11
12
13
## 学生表
| id | name |
|----|------|
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |

## 课程表
| id | title | student_id |
|----|--------|-----------|
| 1 | 数学 | 1 |
| 2 | 语文 | 1 |
| 3 | 英语 | 2 |

查询示例

1
2
3
4
5
6
7
8
SELECT students.name, courses.title 
FROM students
JOIN courses ON students.id = courses.student_id
WHERE students.id = 1;

结果:
张三 | 数学
张三 | 语文

3.2 一对多关联

场景描述:一个用户可以有多个订单记录

1
2
3
4
5
6
7
8
9
10
11
12
13
## 用户表
| user_id | username |
|--------|----------|
| 1 | zhang |
| 2 | li |
| 3 | wang |

## 订单表
| order_id | total_amount | user_id |
|----------|--------------|--------|
| 1001 | 200.50 | 1 |
| 1002 | 150.00 | 1 |
| 1003 | 300.00 | 2 |

查询示例

1
2
3
4
5
6
7
8
SELECT username, total_amount 
FROM users
JOIN orders ON users.user_id = orders.user_id
WHERE user_id = 1;

结果:
zhang | 200.50
zhang | 150.00

3.3 多对多关联

场景描述:员工与部门之间可能存在多对多关系

1
2
3
4
5
6
7
8
9
10
11
12
13
## 部门表
| dept_id | dept_name |
|---------|----------|
| 1 | 教学部 |
| 2 | 后勤部 |
| 3 | 研发部 |

## 员工表
| employee_id | name | dept_id |
|-------------|-------|---------|
| 1 | 张三 | 1 |
| 2 | 李四 | 1 |
| 3 | 王五 | 2 |

查询示例

1
2
3
4
5
6
7
8
SELECT name, dept_name 
FROM employees
JOIN departments ON employees.dept_id = departments.dept_id
WHERE dept_name = '教学部';

结果:
张三 | 教学部
李四 | 教学部

四、设计规范与常见误区

4.1 最佳实践

  • 主键设计规范
    1. 优先使用自增主键,降低写入开销
    2. 避免使用业务字段作为主键
    3. 复合主键应包含最小必要字段
  • 外键设计规范
    1. 外键字段应与主键字段类型完全一致
    2. 合理配置ON DELETE和ON UPDATE行为
    3. 外键约束应与业务逻辑保持一致

4.2 常见误区

  • 错误1:未设置外键约束导致数据不一致
  • 错误2:使用过长的字段作为主键(如UUID)
  • 错误3:忽略外键约束对更新操作的影响
  • 错误4:在不需要时仍保留外键约束

4.3 设计验证

主键验证

  • 检查每张表必须有且仅有一个主键
  • 确认主键字段值唯一性(通过SELECT COUNT(*) FROM table GROUP BY pk_field HAVING COUNT(*) > 1

外键验证

  • 使用SHOW CREATE TABLE table_name查看约束定义
  • 验证外键值必须存在于关联表的主键中
  • 确认约束行为符合业务需求

4.4 实践建议

  • 主键生成策略:优先采用自增ID,对于需要分布式ID的场景可使用UUID
  • 外键管理:定期检查外键约束的有效性
  • 数据一致性:在删除主表记录前,先检查外键关联情况
  • 命名规范:主键字段建议使用id,外键字段建议使用fk_字段名