E-R 模型向关系模式转换:8种场景实战与 MySQL 8.0 建表示例

E-R 模型向关系模式转换:8种场景实战与 MySQL 8.0 建表示例

在数据库设计的逻辑结构设计阶段,将概念模型(E-R图)转换为关系模式是一个关键步骤。这个过程直接决定了数据库的结构是否合理、高效。本文将深入探讨8种典型E-R联系类型的转换方法,并提供可直接运行的MySQL 8.0建表语句示例。

1. 一对一(1:1)联系转换

一对一联系是最简单的实体关系类型之一。在转换时,我们有两种主要方案:

方案一:独立关系模式

CREATE TABLE factory ( factory_id VARCHAR(10) PRIMARY KEY, factory_name VARCHAR(50) NOT NULL, location VARCHAR(100) ); CREATE TABLE manager ( manager_id VARCHAR(10) PRIMARY KEY, name VARCHAR(50) NOT NULL, gender CHAR(1), age INT ); CREATE TABLE management ( factory_id VARCHAR(10) UNIQUE, manager_id VARCHAR(10) UNIQUE, term VARCHAR(20), FOREIGN KEY (factory_id) REFERENCES factory(factory_id), FOREIGN KEY (manager_id) REFERENCES manager(manager_id), PRIMARY KEY (factory_id, manager_id) );

方案二:合并到任意一方

CREATE TABLE factory ( factory_id VARCHAR(10) PRIMARY KEY, factory_name VARCHAR(50) NOT NULL, location VARCHAR(100), manager_id VARCHAR(10) UNIQUE, term VARCHAR(20), FOREIGN KEY (manager_id) REFERENCES manager(manager_id) ); CREATE TABLE manager ( manager_id VARCHAR(10) PRIMARY KEY, name VARCHAR(50) NOT NULL, gender CHAR(1), age INT );

选择建议:当一方参与度较高(如强制参与)时,合并到该方更优;当双方参与度相似且联系有多个属性时,独立模式更清晰。

2. 一对多(1:n)联系转换

一对多联系是数据库中最常见的关系类型,转换时通常将"1"方的主码作为外码加入"n"方。

仓库-商品示例

CREATE TABLE warehouse ( warehouse_id VARCHAR(10) PRIMARY KEY, location VARCHAR(100) NOT NULL, area DECIMAL(10,2) CHECK (area > 0) ); CREATE TABLE product ( product_id VARCHAR(15) PRIMARY KEY, name VARCHAR(100) NOT NULL, price DECIMAL(10,2) CHECK (price >= 0), warehouse_id VARCHAR(10), quantity INT DEFAULT 0, FOREIGN KEY (warehouse_id) REFERENCES warehouse(warehouse_id) );

性能优化技巧

  • warehouse_id上建立索引加速关联查询
  • 考虑使用ON DELETE CASCADEON DELETE SET NULL约束
  • 对于大型系统,可将quantity分离到独立的库存表

3. 多对多(m:n)联系转换

多对多联系必须转换为独立的关系模式,包含关联双方的主码及联系自身的属性。

学生-课程经典示例

CREATE TABLE student ( student_id VARCHAR(12) PRIMARY KEY, name VARCHAR(50) NOT NULL, gender CHAR(1), age INT CHECK (age BETWEEN 15 AND 50), enrollment_date DATE ); CREATE TABLE course ( course_id VARCHAR(8) PRIMARY KEY, title VARCHAR(100) NOT NULL, credit INT DEFAULT 2 CHECK (credit BETWEEN 1 AND 6), description TEXT ); CREATE TABLE enrollment ( student_id VARCHAR(12), course_id VARCHAR(8), grade DECIMAL(5,2) CHECK (grade BETWEEN 0 AND 100), semester VARCHAR(6), enrollment_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (student_id, course_id, semester), FOREIGN KEY (student_id) REFERENCES student(student_id), FOREIGN KEY (course_id) REFERENCES course(course_id) );

设计要点

  • 联合主键确保同一学生不能重复选修同一课程
  • 添加semester字段支持同一课程多次选修
  • enrollment_time记录精确的选课时间

4. 弱实体转换

弱实体是指其存在依赖于其他实体的实体,转换时需要将依赖实体的主码纳入弱实体的主码中。

员工-家属关系示例

CREATE TABLE employee ( employee_id VARCHAR(10) PRIMARY KEY, name VARCHAR(50) NOT NULL, position VARCHAR(50), department VARCHAR(50) ); CREATE TABLE dependent ( employee_id VARCHAR(10), dependent_name VARCHAR(50), relationship VARCHAR(20) NOT NULL, birth_date DATE, PRIMARY KEY (employee_id, dependent_name), FOREIGN KEY (employee_id) REFERENCES employee(employee_id) ON DELETE CASCADE );

关键特征

  • dependent表没有独立的主键
  • 主键由employee_iddependent_name共同组成
  • 使用ON DELETE CASCADE确保员工删除时自动删除家属记录

5. 超类-子类转换

超类子类关系体现面向对象的继承思想,转换时可选择三种方案:

方案一:每个实体单独建表

CREATE TABLE employee ( employee_id VARCHAR(10) PRIMARY KEY, name VARCHAR(50) NOT NULL, hire_date DATE, employee_type ENUM('pilot', 'mechanic', 'administrator') NOT NULL ); CREATE TABLE pilot ( employee_id VARCHAR(10) PRIMARY KEY, flight_hours INT DEFAULT 0, license_number VARCHAR(20) UNIQUE, last_medical_check DATE, FOREIGN KEY (employee_id) REFERENCES employee(employee_id) ); CREATE TABLE mechanic ( employee_id VARCHAR(10) PRIMARY KEY, specialization VARCHAR(50), certification_level VARCHAR(20), FOREIGN KEY (employee_id) REFERENCES employee(employee_id) ); CREATE TABLE administrator ( employee_id VARCHAR(10) PRIMARY KEY, department VARCHAR(50), position VARCHAR(50), FOREIGN KEY (employee_id) REFERENCES employee(employee_id) );

方案二:所有属性合并到超类

CREATE TABLE employee ( employee_id VARCHAR(10) PRIMARY KEY, name VARCHAR(50) NOT NULL, hire_date DATE, employee_type ENUM('pilot', 'mechanic', 'administrator') NOT NULL, -- 飞行员属性 flight_hours INT, license_number VARCHAR(20), last_medical_check DATE, -- 机械师属性 specialization VARCHAR(50), certification_level VARCHAR(20), -- 管理员属性 department VARCHAR(50), position VARCHAR(50), -- 添加约束确保属性一致性 CONSTRAINT chk_pilot CHECK ( employee_type != 'pilot' OR ( flight_hours IS NOT NULL AND license_number IS NOT NULL AND last_medical_check IS NOT NULL ) ) );

方案三:所有属性合并到子类

CREATE TABLE pilot ( employee_id VARCHAR(10) PRIMARY KEY, name VARCHAR(50) NOT NULL, hire_date DATE, flight_hours INT DEFAULT 0, license_number VARCHAR(20) UNIQUE, last_medical_check DATE ); CREATE TABLE mechanic ( employee_id VARCHAR(10) PRIMARY KEY, name VARCHAR(50) NOT NULL, hire_date DATE, specialization VARCHAR(50), certification_level VARCHAR(20) ); CREATE TABLE administrator ( employee_id VARCHAR(10) PRIMARY KEY, name VARCHAR(50) NOT NULL, hire_date DATE, department VARCHAR(50), position VARCHAR(50) );

方案选择依据

  • 子类属性差异大且查询常按类型分离 → 方案一
  • 子类属性少且常需要跨类型查询 → 方案二
  • 子类间几乎无共同属性 → 方案三

6. 同一实体内的1:n联系

这种递归关系表示实体内部的层次结构,如组织架构中的上下级关系。

员工-领导关系示例

CREATE TABLE employee ( employee_id VARCHAR(10) PRIMARY KEY, name VARCHAR(50) NOT NULL, position VARCHAR(50), salary DECIMAL(10,2) CHECK (salary > 0), manager_id VARCHAR(10), performance_rating DECIMAL(3,2), FOREIGN KEY (manager_id) REFERENCES employee(employee_id) );

查询技巧

  • 使用CTE(Common Table Expression)查询多层汇报关系
  • 添加level字段记录层级深度优化查询性能
  • 考虑使用闭包表(Closure Table)模式处理复杂层次关系

7. 同一实体内的m:n联系

这种递归关系表示实体内部的复杂网络关系,如零部件之间的组装关系。

零部件组装关系示例

CREATE TABLE component ( component_id VARCHAR(10) PRIMARY KEY, name VARCHAR(100) NOT NULL, specification TEXT, unit_cost DECIMAL(10,2) CHECK (unit_cost >= 0) ); CREATE TABLE assembly ( parent_id VARCHAR(10), child_id VARCHAR(10), quantity INT NOT NULL CHECK (quantity > 0), notes VARCHAR(200), PRIMARY KEY (parent_id, child_id), FOREIGN KEY (parent_id) REFERENCES component(component_id), FOREIGN KEY (child_id) REFERENCES component(component_id), CONSTRAINT no_self_assembly CHECK (parent_id != child_id) );

防环设计

  • 添加CHECK (parent_id != child_id)防止直接自引用
  • 使用触发器或应用逻辑防止间接循环引用
  • 考虑使用物化路径(Materialized Path)或嵌套集(Nested Set)模型

8. 多实体间的m:n联系

当三个或更多实体参与一个多元联系时,需要创建包含所有相关实体主码的联系表。

供应商-零件-项目供应关系示例

CREATE TABLE supplier ( supplier_id VARCHAR(10) PRIMARY KEY, name VARCHAR(100) NOT NULL, address TEXT, contact_phone VARCHAR(20) ); CREATE TABLE part ( part_id VARCHAR(10) PRIMARY KEY, description VARCHAR(200) NOT NULL, weight DECIMAL(10,3), storage_condition VARCHAR(50) ); CREATE TABLE project ( project_id VARCHAR(10) PRIMARY KEY, title VARCHAR(100) NOT NULL, start_date DATE, deadline DATE, budget DECIMAL(12,2) ); CREATE TABLE supply ( supplier_id VARCHAR(10), part_id VARCHAR(10), project_id VARCHAR(10), quantity INT NOT NULL CHECK (quantity > 0), unit_price DECIMAL(10,2) CHECK (unit_price >= 0), delivery_date DATE, PRIMARY KEY (supplier_id, part_id, project_id), FOREIGN KEY (supplier_id) REFERENCES supplier(supplier_id), FOREIGN KEY (part_id) REFERENCES part(part_id), FOREIGN KEY (project_id) REFERENCES project(project_id) );

设计扩展

  • 添加status字段跟踪供应状态
  • 包含contract_number等业务字段
  • 使用复合索引优化常见查询路径