ER图ERD实体关系图ER图实例数据库设计教程开发者工具

ER 图实例:5 个真实数据库设计详解

5 个实际场景的 ER 图(实体关系图)完整示例——涵盖电商、学校、医院、图书馆和社交平台数据库设计,附实体、属性、关系的详细说明和设计要点。

CodePic Team发布于 2026-05-1312 min read

学 ER 图最快的方式就是看实际例子。实体、属性、基数这些抽象概念,放到你熟悉的场景里——商店、学校、医院——立刻就能理解了。

本文展示 5 个完整的 ER 图实例,每个来自不同领域。每个例子都会覆盖实体、关键属性、实体之间的关系以及值得注意的设计决策。如果你对 ER 图的基础概念还不熟悉,建议先看 ER 图是什么


1. 电商数据库

在线商店需要追踪客户、商品、订单和支付。这可能是课程和面试中最常见的 ER 图类型。

实体:

  • Customer(客户) — id (PK), name, email, phone, address, created_at
  • Product(商品) — id (PK), name, description, price, stock_qty, category_id (FK)
  • Category(分类) — id (PK), name, parent_category_id (FK, 自引用)
  • Order(订单) — id (PK), placed_at, status, shipping_address, customer_id (FK)
  • OrderItem(订单项) — id (PK), order_id (FK), product_id (FK), quantity, unit_price
  • Payment(支付) — id (PK), order_id (FK), method, amount, paid_at, status

关系:

  • Customer → Order:一对多。一个客户可以下多个订单,每个订单属于一个客户。
  • Order → OrderItem:一对多。一个订单包含多个订单项。
  • Product → OrderItem:一对多。一个商品可以出现在多个订单项中。
  • Order → Payment:一对一(如果允许拆分支付则是一对多)。
  • Category → Product:一对多。一个分类下有多个商品。
  • Category → Category:自引用一对多。分类可以嵌套(电子产品 → 笔记本电脑 → 游戏本)。

设计要点:

unit_price 存在 OrderItem 上,而不是查询时从 Product 读取。因为价格会变——查看历史订单时,你需要的是下单时的价格,不是当前价格。这是初学者最常犯的错误之一。

Product 和 Order 之间是多对多关系,通过 OrderItem 这个中间表解决。OrderItem 自带数据(quantity、unit_price),不只是个链接表。


2. 学校/大学数据库

学校系统追踪学生、课程、教师和选课记录。这个例子展示了中间表携带有意义数据的场景。

实体:

  • Student(学生) — id (PK), name, email, enrollment_date, major
  • Course(课程) — id (PK), code, title, credits, department_id (FK)
  • Instructor(教师) — id (PK), name, email, title, department_id (FK)
  • Department(院系) — id (PK), name, head_instructor_id (FK)
  • Enrollment(选课记录) — id (PK), student_id (FK), course_id (FK), semester, grade, enrolled_at

关系:

  • Student → Enrollment → Course:多对多(通过 Enrollment)。学生选多门课,课程有多个学生。Enrollment 记录了每个学生-课程组合的学期和成绩。
  • Instructor → Course:一对多。一个老师教多门课。
  • Department → Course:一对多。课程属于院系。
  • Department → Instructor:一对多。教师属于院系。
  • Department → Instructor(系主任):一对一。每个院系有一个系主任。

设计要点:

Enrollment 是 Student 和 Course 之间的中间表,但它不只是两个外键。它还有 semestergradeenrolled_at——这些数据属于关系本身,不属于任何一个实体。当你的中间表有自己的属性时,就应该给它一个独立的主键,把它当作正式实体来对待。

Department 有个 head_instructor_id 引用 Instructor,而 Instructor 又有 department_id 引用 Department。这形成了循环引用——在 ER 图中没问题,但初始化数据库数据时要注意插入顺序。


3. 医院/医疗数据库

医疗数据库比大多数教学示例展示的要复杂得多。这里做了简化,但保留了核心结构。

实体:

  • Patient(患者) — id (PK), name, date_of_birth, gender, phone, emergency_contact, insurance_id (FK)
  • Doctor(医生) — id (PK), name, specialization, license_number, department_id (FK)
  • Department(科室) — id (PK), name, floor, building
  • Appointment(预约) — id (PK), patient_id (FK), doctor_id (FK), scheduled_at, status, notes
  • MedicalRecord(病历) — id (PK), patient_id (FK), doctor_id (FK), diagnosis, treatment, created_at
  • Insurance(保险) — id (PK), provider_name, policy_number, coverage_type, expiry_date

关系:

  • Patient → Appointment:一对多。一个患者可以有多次预约。
  • Doctor → Appointment:一对多。一个医生处理多个预约。
  • Patient → MedicalRecord:一对多。每次就诊产生一条病历。
  • Doctor → MedicalRecord:一对多。主治医生关联到每条病历。
  • Doctor → Department:多对一。医生属于科室。
  • Patient → Insurance:多对一。多个患者可以共享同一个保险计划。

设计要点:

Appointment 和 MedicalRecord 都关联了 Patient 和 Doctor,但它们代表不同的事情。预约是一个计划中的事件(未来或过去);病历是就诊的临床结果。分开存储让你可以追踪爽约(有预约没病历)和未预约就诊(有病历没预约)。

Insurance 作为独立实体而非 Patient 上的列。这避免了多个家庭成员共享同一保险计划时的数据重复。


4. 图书馆管理数据库

图书馆系统是经典的 ER 图练习题,因为它的结构干净、关系清晰。

实体:

  • Book(图书) — id (PK), isbn, title, published_year, genre, shelf_location
  • Author(作者) — id (PK), name, bio, nationality
  • BookAuthor(图书-作者) — book_id (FK), author_id (FK) [复合主键]
  • Member(读者) — id (PK), name, email, phone, membership_type, joined_at
  • Loan(借阅记录) — id (PK), book_id (FK), member_id (FK), borrowed_at, due_at, returned_at
  • Fine(罚款) — id (PK), loan_id (FK), amount, paid, issued_at

关系:

  • Book → Author:多对多(通过 BookAuthor)。一本书可以有多个作者,一个作者可以写多本书。
  • Member → Loan:一对多。一个读者可以多次借书。
  • Book → Loan:一对多。一本书可以被多次借出(按时间顺序)。
  • Loan → Fine:一对一(或一对零)。逾期的借阅可能关联一笔罚款。

设计要点:

BookAuthor 是纯粹的中间表,没有额外属性——book_id + author_id 的组合键就够了。对比电商的 OrderItem(有 quantity 和 price)和学校的 Enrollment(有 semester 和 grade),区别在于你的中间表是否有自己的属性。

Loan 上的 returned_at 可以为空。如果为空,说明书还没还;如果有值,说明书已归还。这一个字段就让你能同时查询"当前在借"和"历史记录",不需要两张表。

Fine 关联到 Loan 而不是直接关联到 Member。这保留了完整的事件链:某次借阅逾期了,所以产生了罚款。通过 Loan 总能找到对应的读者。


5. 社交平台数据库

社交平台的数据模型一旦加上帖子、评论、点赞、关注、通知,复杂度就上来了。这里是简化版。

实体:

  • User(用户) — id (PK), username, email, display_name, bio, avatar_url, created_at
  • Post(帖子) — id (PK), user_id (FK), content, media_url, created_at, updated_at
  • Comment(评论) — id (PK), post_id (FK), user_id (FK), content, created_at
  • Like(点赞) — id (PK), user_id (FK), post_id (FK), created_at [user_id + post_id 唯一约束]
  • Follow(关注) — id (PK), follower_id (FK → User), following_id (FK → User), created_at
  • Notification(通知) — id (PK), user_id (FK), type, reference_id, is_read, created_at

关系:

  • User → Post:一对多。用户发布多条帖子。
  • Post → Comment:一对多。帖子收到多条评论。
  • User → Comment:一对多。用户写多条评论。
  • User → Like → Post:多对多(通过 Like)。用户可以点赞多个帖子,帖子可以被多人点赞。
  • User → Follow → User:自引用多对多(通过 Follow)。用户关注其他用户。follower_id 和 following_id 都指向 User。
  • User → Notification:一对多。用户收到多条通知。

设计要点:

Follow 是自引用多对多关系。Follow 表有两个外键都指向 User——follower_id(谁关注了)和 following_id(被谁关注)。这和对称关系不同:Alice 关注了 Bob,不代表 Bob 也关注了 Alice。

Like 本质上是 User 和 Post 的中间表,但自带 created_at 来记录点赞时间。(user_id, post_id) 上的唯一约束防止同一用户重复点赞。

Notification 用了一个通用的 reference_id,根据通知类型(type)可以指向帖子、评论或关注。这是一种务实的反范式设计——另一种做法是每种通知类型一张表,更规范但查询更难写。


5 个例子中的共同规律

看完 5 个不同领域的例子,几个规律很明显:

多对多关系一定要用中间表。 电商有 OrderItem,学校有 Enrollment,图书馆有 BookAuthor,社交平台有 Like 和 Follow。在关系型数据库中没有捷径。

中间表经常带有自己的数据。 OrderItem 有 quantity 和 price,Enrollment 有 semester 和 grade。当中间表有自己的属性时,给它独立的主键,把它当正式实体对待。

自引用关系比你想的更常见。 分类层级(电商)、系主任(学校)、用户关注(社交平台)——都涉及一张表引用自己。

可空外键代表可选关系。 Loan 的 returned_at 为空表示书还在借;Fine 关联到 Loan 表示这次借阅逾期了。可空性是设计决策,不是事后补充。


自己动手试试

学 ER 图最好的办法是自己画。选一个你熟悉的领域——餐厅、健身房会员、音乐流媒体——然后走一遍流程:

  1. 找出实体(名词)
  2. 列出每个实体的属性
  3. 找到实体之间的关系
  4. 确定基数(1:1、1:N、M:N)
  5. 需要的地方加中间表
  6. 画出图

可以用我们的 ER 图模板 快速开始——已经预置了常见的实体形状和关系连接线。画图过程的详细教程,可以看 ER 图怎么画

ER 关系图

ER 关系图

试试这个模板

相关文章