
简介这份《人力资源管理系统ER图详解》文档资料面向数据库设计初学者、信息系统课程学习者以及HR系统开发人员帮助读者快速理解实体关系建模在人事管理场景中的实际落地方法。资源仅含1个doc文件压缩包大小约35KB以文字配合ER图形式呈现便于直接阅读与对照。文档围绕职员、部门、招聘信息、考勤信息、工资信息等核心实体展开详细说明了职员与考勤记录的一对多关系、招聘编号如何衔接应聘与录用流程、部门经理角色的人员归属以及部门与招聘职位之间的关联。通过解析1:n、n:1等关系标注与属性划分读者可以掌握从业务需求转换为数据库表结构的完整思路并据此设计出覆盖员工档案、考勤、薪资和招聘等模块的数据模型。目前已有390人学习浏览适合需要快速梳理HR系统数据模型、准备课程设计或数据库作业的学生与开发者参考。1. 人力资源管理系统ER图是什么为什么它是HRMS的第一份设计合同一张人力资源管理系统ER图看着像课程设计里交差的白纸黑字实际上是整个HRMS数据库的第一份设计合同。跳过ER图直接建表的人十有八九会在部门层级、员工与账号拆分、薪资历史这三处返工而且返工要动到所有关联表比画图时多花的时间贵得多。它要回答三件事哪些业务对象要落库、对象之间是什么关系、每张表以谁为主键。适合正准备做HRMS的开发者、写数据库大作业的学生以及想把手里的Excel人事台账整理成系统表的选型人。看懂它后面建表、写接口、对接权限才有统一的地图。2. HRMS数据库建模第一步把业务对象抽成实体、关系与基数2.1 先圈边界HRMS到底管哪几件事很多ER图翻车不是不会画矩形和菱形而是边界没圈住。人力资源管理系统常见的业务边界是七块组织架构、员工中心、考勤、薪酬、招聘、培训、绩效。把这些都塞进一张ER图实体能超过20个关系线密密麻麻交付时根本没法评审。常见做法是先分成两个域主数据域和业务流水域。主数据域画的是当前是什么部门、岗位、员工、用户账号这类数据变化慢一条记录代表一个当下存在的对象。业务流水域画的是发生过什么考勤记录、薪资记录、培训档案、简历投递这类数据只追加、不覆盖一条记录代表一次事实。ER图里把这两个域分开评审时先看图例再看域内关系十分钟就能判断设计是否合理。边界圈完再抽实体才不会把招聘模块的十几个表全堆进一张图。2.2 实体清单与属性表11个核心实体的主键、外键一次定死圈完边界下一步是列实体清单。这里不追求一次列全但要把一定会出现的核心实体定下来。以一套中小型HRMS为例核心实体通常落在下面11张表上画ER图时主键、外键在这个阶段就要写清楚。实体主键关键字段典型外键部门 departmentdept_iddept_name, parent_idparent_id 自关联, manager_id 逻辑外键岗位 positionposition_idposition_code, position_namedept_id员工 employeeemp_idemp_no, name, hire_date, statusdept_id, position_id用户账号 user_accountuser_idusername, password_hash, roleemp_id与员工一对一考勤记录 attendanceattendance_idwork_date, check_in_time, check_out_timeemp_id薪资记录 salary_recordsalary_idperiod, base_salary, net_salaryemp_id培训课程 training_coursecourse_idcourse_name, course_type, duration无培训档案 training_recordrecord_idscore, finish_dateemp_id, course_id招聘职位 recruit_jobrecruit_idheadcount, close_dateposition_id简历 resumeresume_idcandidate_name, phone, statusrecruit_id绩效记录 performance_recordperf_idperiod, score, rankemp_id几个容易起争议的点在清单阶段就要拍板。员工的身份证号id_card业务上要求唯一但历史上存在重号或录入错误所以只建普通索引不建唯一约束。工号emp_no才是业务唯一键用UNIQUE KEY表达。部门表的manager_id指向员工而员工表又挂在部门下这叫循环引用清单阶段先标记为逻辑外键建表时不加物理约束否则建表顺序会卡死。这些决定写进ER图旁边的备注评审时一眼能看见。2.3 关系与基数把1:1、1:N、M:N落到业务事实上实体抽完开始画关系线。关系线的核心是基数基数不是画图软件里随便点的要从业务事实上推。人力资源管理系统里常见的关系就三类。员工到部门是N:1一个部门下有多个员工一个员工只属于一个部门部门到部门是1:N的自关联一个部门有一个上级部门一个上级部门管多个下级部门岗位到员工是1:N一个岗位能挂多个员工。员工到用户账号是1:1一个员工有一个登录账号这个关系最容易画错因为很多系统里一个员工确实可能有两个账号但ER图表达业务模型时按1:1建模账号需求变化靠系统表扩展不靠业务实体扩展。员工到考勤、员工到薪资、员工到绩效都是1:N一条员工记录对应多条流水记录。这里要提醒一个高频错误员工和培训课程是M:N一个员工可以参加多门课程一门课程可以被多个员工参加ER图上不能让员工表和课程表直接连线必须引入培训档案作为中间实体把M:N拆成两个1:N。招聘职位到简历是1:N一个职位收到多份简历。把这一段捋完ER图的关系线才是有业务根据的不是画着好看的。3. 手把手画HRMS的ER图从概念草图到MySQL Workbench成图3.1 概念ER图先画业务实体框、属性椭圆、关系菱形ER图怎么画才不是花架子答案是不从矩形开始从业务事实开始。数据库系统概论里的ER图例题通常拿学生、课程、选课三实体讲十几分钟能画完一到人力资源管理系统这种实体数量是例题三倍的场景很多人崩在不知道先画什么。我的习惯是先画概念ER图只表达业务存在不碰主键、不碰外键。概念ER图的画法很传统实体用矩形属性用椭圆关系用菱形。比如部门矩形连一个部门名称椭圆连一个上级部门椭圆员工矩形连工号姓名入职日期椭圆部门和员工之间画一个菱形写上包含员工端标1部门端标N。这个阶段不要考虑M:N怎么拆先把业务上有什么、谁和谁发生了关系摆清楚。概念图的价值在评审。拿着概念图去问HR业务人员员工是不是一定属于某个部门比拿着一张布满主键外键的物理图去问高效得多。概念图画完确认业务事实没被扭曲再进入逻辑建模把椭圆属性逐个落成具体字段把菱形关系落成外键和中间表。跳过概念图直接建物理ER图最容易出现两边信息不对称画图的按自己想当然建模看图的以为画的就是业务现状。3.2 用MySQL Workbench把草图变成规范ER图概念图确认后我一般用MySQL Workbench做正式建模。早期教材里常用Rational Rose画ER图工具本身没问题但安装和导出SQL都不够轻量新项目里已经很少见。现在更常见的选择是draw.io和MySQL Workbench前者适合快速出图后者胜在ER模型和SQL能互相转换。人力资源管理系统这种需要落库的项目我推荐MySQL Workbench。具体步骤第一步打开MySQL Workbench在首页点加号新建一个EER Model进入建模画布。第二步从左侧工具栏拖入实体双击实体框进入编辑状态把表名改成employee逐列添加字段并勾选主键。注意这里定义的是物理列不是概念属性所以要同时确定数据类型工号用VARCHAR(20)出生日期用DATE薪资用DECIMAL(10,2)。第三步用工具栏上的Relationships 1:n连线工具连接两张表。点击顺序有讲究先点1端的表再点N端的表。它的含义是这条关系里哪边是多方点反了外键会落在错误的表上。第四步双击关系线在Foreign Key面板里确认外键列名比如员工表的dept_id以及Referenced Table是department。HRMS这类业务系统几乎全部使用Non-Identifying Relationship也就是子表主键不从父表继承因为员工ID、部门ID各自独立生成符合直觉也方便ORM映射。第五步画完保存后续可以通过File→Export导出成图片也可以直接生成SQL脚本。这一整套流程走完ER图就从一个给老师看的示意图变成了能直接指导建表的施工图。3.3 局部放大组织架构与员工主数据的关系线怎么走全图太大难以下手时我会先挑一个局部画透比如组织架构与员工主数据这个区域。这个局部包含三个实体、四条关系线是整个HRMS的锚点画好后其他模块都挂在这棵树上。部门和岗位是1:N一个部门下有多个岗位。部门和员工是1:N一个部门下有多个员工。岗位和员工是1:N一个岗位能挂多个员工。部门和部门是自关联1:N用parent_id表达。这里会冒出一个值得注意的设计分歧既然员工通过岗位也关联到了部门那员工表的dept_id是不是冗余两种做法在真实项目里都存在。一种走规范路线员工不直接挂部门部门关系从岗位带出好处是避免两个外键指向同一棵部门树数据一致性维护点少另一种走实用路线员工表保留dept_id因为HR系统里按部门汇总考勤、薪资、绩效是最高频的查询直接join employee.dept_id比绕道position再join department快得多也方便处理借调员工临时挂靠的情况。我一般选实用路线代价就是多一个外键约束但换来的是查询简洁。这个取舍建议写进ER图的备注评审时最容易被问到。4. 把ER图落成建表SQL核心表结构与外键参数选型4.1 建表顺序先建无外键依赖的表循环引用用逻辑外键ER图画完接下来是把它翻译成建表SQL。这个环节最常见的翻车是把建表顺序搞错尤其是部门表和员工表之间的循环引用department表需要manager_id指向employeeemployee表需要dept_id指向department。如果两边都建物理外键无论先建谁都失败。解决方式有两种。第一种manager_id不建物理外键只建普通索引由应用层保证它的值来自员工表查部门负责人时用一个LEFT JOIN带出员工信息效果完全等价。第二种分两步建表先建不含manager_id的department再建employee最后用ALTER TABLE给department补外键。实际项目里我更推荐前者因为循环外键在ORM反向生成实体时也容易造成映射死锁。ER图上把这个设计意图标注成逻辑外键物理模型和逻辑模型就不打架了。建表顺序最终是先department再position再employee再user_account之后是考勤、薪资等流水表最后是培训课程和培训档案这类有中间关系的表。4.2 主数据五表department、position、employee、user_account的建表SQL以核心主数据为例给出可直接套用的建表SQL。-- 部门表parent_id 表达自关联层级manager_id 是逻辑外键不建物理约束 CREATE TABLE department ( dept_id INT UNSIGNED AUTO_INCREMENT COMMENT 部门ID主键, dept_name VARCHAR(50) NOT NULL COMMENT 部门名称, parent_id INT UNSIGNED DEFAULT NULL COMMENT 上级部门ID自关联, manager_id INT UNSIGNED DEFAULT NULL COMMENT 部门负责人ID逻辑外键-employee.emp_id, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (dept_id), KEY idx_parent (parent_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT部门表;这段SQL的字段类型都按HRMS场景考虑过。dept_id用INT UNSIGNED去掉负数区间避免主键被填成负数parent_id允许NULL顶层部门没有上级manager_id只建索引不建外键规避循环依赖。需要强调的一点是这里没有写UNIQUE KEY dept_name因为同一集团下可能存在技术部和技术中心这种名称相近但实际不同的部门名称唯一约束会让维护部门的人骂街。-- 岗位表挂在部门下岗位编码业务唯一 CREATE TABLE position ( position_id INT UNSIGNED AUTO_INCREMENT COMMENT 岗位ID主键, position_code VARCHAR(20) NOT NULL COMMENT 岗位编码, position_name VARCHAR(50) NOT NULL COMMENT 岗位名称, dept_id INT UNSIGNED NOT NULL COMMENT 所属部门ID, salary_min DECIMAL(10,2) DEFAULT NULL COMMENT 薪资下限, salary_max DECIMAL(10,2) DEFAULT NULL COMMENT 薪资上限, PRIMARY KEY (position_id), UNIQUE KEY uk_position_code (position_code), KEY idx_dept (dept_id), CONSTRAINT fk_position_dept FOREIGN KEY (dept_id) REFERENCES department (dept_id) ON DELETE RESTRICT ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT岗位表;岗位表的position_code用唯一约束是因为岗位编码是业务上的人力资源口径一个编码只对应一个岗位。dept_id的外键用ON DELETE RESTRICT表示有岗位的部门不允许直接删除必须先处理岗位或转移岗位所在部门这是从数据完整性角度必须守住的底线。-- 员工主数据表工号唯一部门外键禁止删除岗位外键允许置空 CREATE TABLE employee ( emp_id INT UNSIGNED AUTO_INCREMENT COMMENT 员工ID主键, emp_no VARCHAR(20) NOT NULL COMMENT 工号业务唯一, name VARCHAR(50) NOT NULL COMMENT 姓名, gender TINYINT DEFAULT 0 COMMENT 性别0未知 1男 2女, birth_date DATE DEFAULT NULL COMMENT 出生日期, id_card CHAR(18) DEFAULT NULL COMMENT 身份证号, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, email VARCHAR(100) DEFAULT NULL COMMENT 邮箱, hire_date DATE NOT NULL COMMENT 入职日期, dept_id INT UNSIGNED NOT NULL COMMENT 所属部门ID, position_id INT UNSIGNED DEFAULT NULL COMMENT 岗位ID, status TINYINT NOT NULL DEFAULT 1 COMMENT 在职状态1在职 0离职, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (emp_id), UNIQUE KEY uk_emp_no (emp_no), KEY idx_dept (dept_id), KEY idx_position (position_id), CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES department (dept_id) ON DELETE RESTRICT, CONSTRAINT fk_emp_position FOREIGN KEY (position_id) REFERENCES position (position_id) ON DELETE SET NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT员工主数据表;员工表是HRMS里被引用最多的表字段设计上两个细节值得说明。gender用TINYINT而不是CHAR(1)省空间且避免字符集比较开销status用TINYINT加注释离职员工记录不删除只是状态置0。外键策略上员工和部门用RESTRICT部门不能在有员工的情况下被删员工和岗位用SET NULL岗位被撤销后员工记录仍然保留岗位字段置空避免岗位删除连带员工数据一起消失。入职日期hire_date设为NOT NULL因为这是所有在职统计的起点。-- 用户账号表与员工一对一登录相关的系统字段独立存放 CREATE TABLE user_account ( user_id INT UNSIGNED AUTO_INCREMENT COMMENT 账号ID主键, emp_id INT UNSIGNED NOT NULL COMMENT 员工ID一对一, username VARCHAR(50) NOT NULL COMMENT 登录名, password_hash VARCHAR(255) NOT NULL COMMENT 密码散列值, role VARCHAR(30) DEFAULT staff COMMENT 角色admin/hr/manager/staff, status TINYINT DEFAULT 1 COMMENT 账号状态1启用 0禁用, last_login_time DATETIME DEFAULT NULL COMMENT 最近登录时间, PRIMARY KEY (user_id), UNIQUE KEY uk_emp (emp_id), UNIQUE KEY uk_username (username), CONSTRAINT fk_account_emp FOREIGN KEY (emp_id) REFERENCES employee (emp_id) ON DELETE RESTRICT ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户账号表;user_account单独成表而不是把username和password放进employee是因为两者的变更频率和管理权限完全不同。员工信息由HR维护账号信息由系统管理员维护员工离职再入职时employee会产生一条新记录而账号体系需要保留历史审计依据。emp_id上建唯一键把ER图里的1:1关系直接落成数据库约束防止出现一个员工两个账号的脏数据。4.3 流水与中间表考勤、薪资、培训记录的SQL与外键策略主数据表之后是流水表。考勤和薪资是典型的1:N流水培训档案是M:N关系的中间表。-- 考勤流水表一个员工一天一条记录重复打卡按最新值更新 CREATE TABLE attendance ( attendance_id INT UNSIGNED AUTO_INCREMENT COMMENT 考勤ID主键, emp_id INT UNSIGNED NOT NULL COMMENT 员工ID, work_date DATE NOT NULL COMMENT 出勤日期, check_in_time DATETIME DEFAULT NULL COMMENT 上班打卡时间, check_out_time DATETIME DEFAULT NULL COMMENT 下班打卡时间, status TINYINT DEFAULT 1 COMMENT 1正常 2迟到 3早退 4缺勤, overtime_hours DECIMAL(4,1) DEFAULT 0 COMMENT 加班小时数保留1位小数, PRIMARY KEY (attendance_id), UNIQUE KEY uk_emp_date (emp_id, work_date), KEY idx_work_date (work_date), CONSTRAINT fk_att_emp FOREIGN KEY (emp_id) REFERENCES employee (emp_id) ON DELETE RESTRICT ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT考勤记录表;考勤表在ER图里是纯1:N落库时通常还要补一个唯一约束uk_emp_date这是概念ER图不会直接画出来的实现细节。它在数据库层面保证一个员工一天只能有一条考勤总记录打卡明细去流水表存。overtime_hours用DECIMAL(4,1)能存最大999.9小时对一个月的加班累计足够避免FLOAT带来的小数误差。-- 薪资流水表按员工期间唯一发薪历史只追加不覆盖 CREATE TABLE salary_record ( salary_id INT UNSIGNED AUTO_INCREMENT COMMENT 薪资ID主键, emp_id INT UNSIGNED NOT NULL COMMENT 员工ID, period CHAR(7) NOT NULL COMMENT 薪资期间格式YYYY-MM, base_salary DECIMAL(10,2) DEFAULT NULL COMMENT 基本工资, bonus DECIMAL(10,2) DEFAULT 0 COMMENT 奖金, deduction DECIMAL(10,2) DEFAULT 0 COMMENT 扣款, net_salary DECIMAL(10,2) DEFAULT NULL COMMENT 实发工资, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (salary_id), UNIQUE KEY uk_emp_period (emp_id, period), CONSTRAINT fk_salary_emp FOREIGN KEY (emp_id) REFERENCES employee (emp_id) ON DELETE RESTRICT ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT薪资流水表;薪资的period用CHAR(7)而不是DATE或DATETIME是因为薪资期间是一个年-月的业务概念比如2025-06本身不存储日或时间。用DATE存还得额外约束天数为1号用VARCHAR可能混入脏格式CHAR(7)配合应用层校验最干净。net_salary可以在应用层算好写入也可以由SQL计算实际项目建议应用层算完再落库方便留审计日志。-- 培训档案中间表把员工与课程的多对多拆成两个一对多 CREATE TABLE training_record ( record_id INT UNSIGNED AUTO_INCREMENT COMMENT 培训记录ID主键, emp_id INT UNSIGNED NOT NULL COMMENT 员工ID, course_id INT UNSIGNED NOT NULL COMMENT 课程ID, score DECIMAL(5,2) DEFAULT NULL COMMENT 考核得分, finish_date DATE DEFAULT NULL COMMENT 完成日期, PRIMARY KEY (record_id), UNIQUE KEY uk_emp_course (emp_id, course_id), CONSTRAINT fk_train_emp FOREIGN KEY (emp_id) REFERENCES employee (emp_id) ON DELETE RESTRICT, CONSTRAINT fk_train_course FOREIGN KEY (course_id) REFERENCES training_course (course_id) ON DELETE RESTRICT ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT培训记录表;中间表是ER图上M:N关系的落地形式设计时有一个容易被忽略的点中间表的主键应该用独立的record_id而不是用emp_id和course_id联合做主键。虽然联合键也能保证唯一但员工会在不同年份重复参加同一门课程比如每年一次的安全培训联合主键会直接挡住这种合法需求。所以唯一约束可以保留主键必须独立。score字段属于中间表而不是员工表或课程表因为它是这个员工学这门课产生的结果天然放在关系上。5. HRMS ER图设计避坑6个高频翻车现场与对策5.1 部门自关联画错根因是没给关系线起角色名现象部门表在ER图上画自关联时关系线一头连着dept_id另一头也连着dept_id但没有标注是上级部门还是下级部门看图的根本分不清谁是谁。原因建模工具里自关联必须指定角色名很多人漏了这一步导致同一张表的两条关系线语义完全模糊。画图软件不报错这种错误就一路带到了物理模型。解决在关系线上双击把外键命名为parent_id角色名写成上级部门关系基数确认是一端1、一端N。文档里补一句业务说明一个部门可以有一个上级部门一个上级部门下可以有多个下级部门。这样自关联的语义就锁死了。5.2 员工表与用户账号表合并成一张大宽表现象ER图上只有employee一张表username、password_hash、role这些字段全堆在员工实体里登录代码直接查员工表。原因开发时为了少join一张表把系统账号和业务员工混在同一个实体里。短期看查询快长期看员工隐私字段和账号字段的访问权限无法分离HR改员工资料时理论上能碰到密码字段。解决严格按照业务事实拆表员工和账号是1:1但1:1不代表必须合并。员工表只放人事字段账号表放登录字段。ER图上画出两条矩形和一根关系线落库时用唯一约束保证两边不重复。这个坑几乎每个HRMS都会踩一次拆表是唯一稳定的解法。5.3 薪资、考勤流水被塞进员工表历史数据被update覆盖现象员工实体上直接画了current_salary和monthly_overtime两个属性薪资调整时一条UPDATE把之前的薪资覆盖掉月底做工资流水时发现无据可查。原因把当前状态和历史事实混为一谈。ER图设计时脑子里只想着员工现在拿多少钱没想员工过去每个月拿多少钱。解决把这类时间维度数据全部建模成独立的流水实体。员工表保留当前基本工资用于日常展示薪资记录单独成表按期间存储考勤记录单独成表按日期存储。流水表的唯一键通常是员工期间或员工日期保证同一期间不会出现两条互相打架的记录。这点在ER图评审时一定要追问凡是字段名带time、date、period的都要确认它到底是状态还是流水。5.4 M:N关系漏中间表员工培训变成只能选一门课现象员工实体上画了一个course_id字段或者课程实体上画了一个emp_id字段一张表存不下两个人同时上一门课的数据业务上变成这个课程只能归属一个员工。原因概念ER图阶段没有识别出员工和课程之间的M:N关系或者识别出来了嫌中间表麻烦想用外键字段硬扛。解决在ER图上明确画中间实体常见名字是培训档案training_record它既不是员工的属性也不是课程的属性。中间表上再挂成绩、完成日期这类由某人学某课产生的属性。M:N一旦被正确拆解数据库层面就不会出现一门课只有一个员工这种荒谬限制。这是ER图怎么画多对多的标准答案。5.5 status字段让ER图关系线失真现象ER图上所有表都干干净净没有任何status或is_deleted属性实际建表时每张表都加了一个逻辑删除标志。结果就是图上表达的关系和数据库实际约束不一致。原因画ER图时习惯只画业务概念字段把系统实现字段全部省略但省略后关系线表达的删除语义就失真了。比如员工外键用RESTRICT业务上员工可以离职离职不是删除记录而是把status置0这和外键约束并不冲突但看图的人会误以为员工不能离开部门。解决ER图图例里加一行统一说明全模型表均含status逻辑删除标志ER图省略该列外键关系按物理删除约束表达。这个说明放在文档首页评审时先说清楚后面就不会有人对着关系线纠结为什么员工删不掉。逻辑删除本身不改变外键策略RESTRICT仍然成立因为离职员工记录还在。5.6 ER图与建表SQL对不上图成了装饰品现象图上是M:N画了中间表SQL里没建SQL里多了一张绩效明细表图上找不到对应矩形。图归图库归库二者完全脱节。原因手工维护两张画布MySQL Workbench里的模型更新了但文档里的ER图还是一个月前导出的旧版本或者反过来。人肉同步必然漏。解决交付文档前用建模工具的反向工程功能做一次交叉校验。MySQL Workbench里对已建好的库执行Database→Reverse Engineer能一键把当前物理表反向生成ER图这就是常说的从mysql的表导出er关系图的标准做法。把反向生成的图与手绘ER图并排对比逐表核对表名、字段、外键任何一边多出来的内容都会立刻暴露。保证图与库一致ER图才不是挂在文档里的装饰品。6. 交付前的最后一道工序ER图完整性检查与doc导出技巧6.1 交付前的五步检查ER图画完建表SQL也落完我在交付doc前会过一遍五步检查。第一步看每个实体框是否都有主键主键是否加了标识第二步看所有1:N关系线的N端是否都有外键字段1端是否干净一张表如果两边都挂了外键就要想想是不是冗余关系第三步确认每个M:N关系都被中间表拆掉残留在实体上的多值属性一律不接受第四步逐条外键确认删除策略RESTRICT、SET NULL、CASCADE分别用在哪些线上要能说出业务理由第五步检查流水表是否都有期间或日期属性没有日期字段的流水表基本可以判定为设计缺陷。五步走完问题基本浮出水面。6.2 让ER图在doc里真正可读导出与标注技巧最终交付到Word文档里的ER图建议用MySQL Workbench导出成SVG而不是PNG。SVG在Word里缩放不会模糊A3纸横版排版时缩小到一页打印出来关系线仍然清晰。导出路径是File→Export→Export as SVG。如果走的是先建表再补图的路子用Reverse Engineer反向生成的图更贴合实际库结构省去手工对齐的时间。想快速自查关系是否正确时我也会用mermaid的er图语法在笔记里画一版草稿确认实体和基数后再回正式工具整理。mermaid适合自查因为它改起来快但不适合作为最终交付物格式和排版在正式文档里撑不住。这是工具定位的问题不是mermaid本身不好用。我自己的交付习惯是在文档右下角留一行小字本图与建表SQL由同一模型生成修改任何一方请同步另一方。这行字不吓人但每次有人想只改SQL不改图时都会看到它。设计图这种东西最怕的就是图和库悄悄分家。希望这篇文章能让你少走一段建模的弯路画出一张能真正指导开发的HRMS ER图。本文还有配套的精品资源点击获取