一、项目背景
随着企业招聘规模不断扩大,招聘过程中会产生大量候选人、岗位、申请记录以及员工入职等数据。
通过对招聘数据进行系统分析,可以从招聘渠道、招聘状态、岗位、部门、学历等多个维度了解企业招聘情况,为招聘渠道优化、岗位需求调整以及招聘效率提升提供数据支持。
本项目以一组模拟企业招聘数据为基础,使用MySQL完成数据导入、数据清洗与数据分析,并使用Power BI对分析结果进行可视化展示,最终制作招聘数据分析 Dashboard。
项目整体流程如下:
原始CSV数据 ↓ MySQL数据导入 ↓ 数据质量检查 ↓ 数据清洗 ↓ SQL多维度分析 ↓ Excel整理分析结果 ↓ Power BI可视化 ↓ 招聘数据分析Dashboard二、项目目标
本项目主要希望解决以下几个问题:
不同招聘渠道的候选人数和录用率有什么差异?
当前招聘流程中,各种招聘状态的申请记录如何分布?
哪些岗位申请人数较多?哪些岗位录用率较高?
不同部门的招聘表现有什么差异?
不同学历候选人的录用情况是否存在明显差异?
如何通过 Power BI 将分析结果进行可视化展示?
最终希望通过数据分析发现招聘过程中的特点,为招聘渠道选择、岗位招聘策略和人力资源配置提供参考。
三、数据准备
3.1 数据表设计
本项目共使用4张核心数据表:
candidates:候选人信息表jobs:岗位信息表applications:职位申请记录表employees:员工信息表
1. candidates——候选人信息表
主要字段:
| 字段 | 含义 |
|---|---|
| candidate_id | 候选人编号 |
| candidate_name | 候选人姓名 |
| age | 年龄 |
| gender | 性别 |
| school | 学校 |
| major | 专业 |
| work_years | 工作年限 |
| education | 学历 |
2. jobs——岗位信息表
主要字段:
| 字段 | 含义 |
|---|---|
| job_id | 岗位编号 |
| job_name | 岗位名称 |
| department | 所属部门 |
| city | 工作城市 |
| salary | 薪资 |
| job_status | 岗位状态 |
3. applications——职位申请记录表
主要字段:
| 字段 | 含义 |
|---|---|
| application_id | 申请记录编号 |
| candidate_id | 候选人编号 |
| job_id | 岗位编号 |
| apply_date | 申请日期 |
| application_status | 招聘状态 |
| source | 招聘渠道 |
4. employees——员工信息表
主要字段:
| 字段 | 含义 |
|---|---|
| employee_id | 员工编号 |
| candidate_id | 候选人编号 |
| department | 所属部门 |
| position | 职位 |
| city | 工作城市 |
| entry_date | 入职日期 |
| salary | 薪资 |
【插图②:Navicat 中4张数据表截图】
四、MySQL环境搭建
本项目使用MySQL + Navicat完成数据库操作。
首先创建数据库:
CREATE DATABASE hr_analysis CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;进入数据库:
USE hr_analysis;之后将候选人、岗位、申请记录和员工数据分别导入对应的数据表。
五、数据导入与字符集问题处理
在进行数据导入时,曾遇到中文数据无法正常写入的问题。
报错信息类似:
1366 - Incorrect string value经过检查发现,数据表默认字符集为latin1,而 CSV 文件中包含大量中文数据,因此出现字符集不兼容问题。
可以通过以下 SQL 查看数据表字符集:
SHOW CREATE TABLE candidates;如果发现:
DEFAULT CHARSET=latin1则需要将数据表转换为utf8mb4。
例如:
ALTER TABLE candidates CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;其他包含中文字段的数据表也采用同样方式处理。
处理完成后重新导入数据,并通过查询检查中文数据是否正常。
SELECT * FROM candidates LIMIT 10;六、数据清洗与质量检查
数据分析之前,需要先对原始数据进行质量检查。
本项目主要从以下几个方面进行检查:
主键重复
缺失值
数值异常
数据范围
表之间的关联关系
业务重复
日期逻辑
6.1 主键重复检查
首先检查候选人编号是否存在重复:
SELECT candidate_id, COUNT(*) AS cnt FROM candidates GROUP BY candidate_id HAVING COUNT(*) > 1;岗位编号:
SELECT job_id, COUNT(*) AS cnt FROM jobs GROUP BY job_id HAVING COUNT(*) > 1;申请记录编号:
SELECT application_id, COUNT(*) AS cnt FROM applications GROUP BY application_id HAVING COUNT(*) > 1;员工编号:
SELECT employee_id, COUNT(*) AS cnt FROM employees GROUP BY employee_id HAVING COUNT(*) > 1;检查结果显示,核心编号不存在重复,因此可以继续进行后续分析。
七、缺失值检查
对主要字段进行 NULL 和空字符串检查。
例如候选人信息表:
SELECT COUNT(*) AS total_rows, SUM(candidate_id IS NULL OR candidate_id = '') AS candidate_id_missing, SUM(candidate_name IS NULL OR candidate_name = '') AS name_missing, SUM(age IS NULL) AS age_missing, SUM(gender IS NULL OR gender = '') AS gender_missing, SUM(school IS NULL OR school = '') AS school_missing, SUM(major IS NULL OR major = '') AS major_missing, SUM(work_years IS NULL) AS work_years_missing, SUM(education IS NULL OR education = '') AS education_missing FROM candidates;对其他数据表进行同样检查。
检查结果显示,本项目核心字段不存在明显缺失值,因此不需要进行大规模缺失值填补。
八、数值合理性检查
8.1 候选人年龄检查
首先查看候选人的年龄范围:
SELECT MIN(age) AS 最小年龄, MAX(age) AS 最大年龄, ROUND(AVG(age), 2) AS 平均年龄 FROM candidates;结果:
最小年龄:21岁
最大年龄:35岁
平均年龄:约28.18岁
整体处于合理范围。
8.2 工作年限检查
检查工作年限:
SELECT MIN(work_years) AS 最小工作年限, MAX(work_years) AS 最大工作年限, ROUND(AVG(work_years), 2) AS 平均工作年限 FROM candidates;进一步根据业务逻辑检查:
SELECT candidate_id, age, work_years FROM candidates WHERE work_years > age - 18;发现部分记录存在工作年限大于“年龄减18”的情况。
这里采用一个简单的业务假设:
假设候选人最早从18岁开始工作。
因此将超过合理范围的工作年限修正为:
年龄 - 18在修改之前先创建备份表:
CREATE TABLE candidates_backup AS SELECT * FROM candidates;然后进行修正:
UPDATE candidates SET work_years = age - 18 WHERE work_years > age - 18;修正后重新检查:
SELECT candidate_id, age, work_years FROM candidates WHERE work_years > age - 18;检查结果为0条异常记录。
需要说明的是,该规则属于本项目中的业务假设,实际企业数据中应根据员工真实工作经历、毕业时间等信息进行判断,而不能简单按照年龄进行修改。
九、薪资数据检查
检查岗位薪资:
SELECT MIN(salary) AS 最低薪资, MAX(salary) AS 最高薪资, ROUND(AVG(salary), 2) AS 平均薪资 FROM jobs;检查是否存在小于等于0的薪资:
SELECT COUNT(*) AS 异常数量 FROM jobs WHERE salary <= 0;结果显示:
最低薪资:6066
最高薪资:19301
平均薪资:约12325.40
异常薪资数量:0
员工薪资同样进行检查:
SELECT MIN(salary) AS 最低薪资, MAX(salary) AS 最高薪资, ROUND(AVG(salary), 2) AS 平均薪资 FROM employees;检查结果:
最低薪资:6008
最高薪资:17962
平均薪资:约12284.00
不存在明显异常。
十、关联关系检查
由于本项目涉及多张数据表,因此还需要检查表之间的关联关系。
10.1 applications 与 candidates
检查申请记录中的候选人是否都存在:
SELECT COUNT(*) AS invalid_count FROM applications a LEFT JOIN candidates c ON a.candidate_id = c.candidate_id WHERE c.candidate_id IS NULL;结果为:
0说明申请记录中的候选人均可以在候选人信息表中找到。
10.2 applications 与 jobs
SELECT COUNT(*) AS invalid_count FROM applications a LEFT JOIN jobs j ON a.job_id = j.job_id WHERE j.job_id IS NULL;结果为0。
10.3 employees 与 candidates
SELECT COUNT(*) AS invalid_count FROM employees e LEFT JOIN candidates c ON e.candidate_id = c.candidate_id WHERE c.candidate_id IS NULL;结果同样为0。
因此,核心表之间的关联关系整体正常。
十一、业务重复检查
除了检查主键重复,还需要检查业务层面的重复。
例如同一个候选人是否多次申请同一个岗位:
SELECT candidate_id, job_id, COUNT(*) AS application_count FROM applications GROUP BY candidate_id, job_id HAVING COUNT(*) > 1;检查发现部分候选人存在多次申请同一个岗位的情况。
进一步查看具体记录,例如:
SELECT * FROM applications WHERE candidate_id = 'C00006' AND job_id = 'J0092';可以发现同一候选人在不同日期存在不同申请记录,例如:
一次申请状态为 Rejected
后续再次申请状态为 Hired
因此,这类记录并不能简单认定为重复数据。
本项目最终将其保留,并将其理解为:
业务重复 ≠ 数据重复
即同一候选人可能在不同时间重新申请同一岗位,因此不能仅通过candidate_id + job_id判断数据重复。
十二、日期逻辑检查
申请日期范围:
SELECT MIN(apply_date) AS 最早申请日期, MAX(apply_date) AS 最晚申请日期 FROM applications;结果:
2026-01-01 ~ 2026-06-30员工入职日期范围:
SELECT MIN(entry_date) AS 最早入职日期, MAX(entry_date) AS 最晚入职日期 FROM employees;结果:
2026-02-01 ~ 2026-07-31进一步对候选人的申请时间与入职时间进行业务逻辑检查。
由于一个候选人可能存在多次申请记录,因此不能直接使用任意一条申请记录进行判断,而是使用该候选人的最早申请日期进行检查。
SELECT e.employee_id, e.candidate_id, e.entry_date, MIN(a.apply_date) AS first_apply_date FROM employees e JOIN applications a ON e.candidate_id = a.candidate_id GROUP BY e.employee_id, e.candidate_id, e.entry_date HAVING e.entry_date < MIN(a.apply_date);部分数据存在入职日期早于最早申请日期的情况。
由于当前数据缺少完整的招聘流程时间节点,且数据本身属于项目模拟数据,因此本项目不直接修改这些记录,而是在数据质量检查阶段进行记录。
这也说明:
数据清洗并不意味着所有异常数据都必须删除或修改,而是需要结合业务逻辑判断异常产生的原因。
十三、招聘渠道分析
完成数据清洗后,开始进行招聘数据分析。
首先分析不同招聘渠道的候选人数、录用人数和录用率。
SQL代码:
SELECT source AS 招聘渠道, COUNT(DISTINCT candidate_id) AS 候选人数, COUNT(DISTINCT CASE WHEN application_status = 'Hired' THEN candidate_id END) AS 录用人数, ROUND( COUNT(DISTINCT CASE WHEN application_status = 'Hired' THEN candidate_id END) / COUNT(DISTINCT candidate_id) * 100, 2 ) AS 录用率 FROM applications GROUP BY source ORDER BY 录用率 DESC;分析结果:
| 招聘渠道 | 候选人数 | 录用人数 | 录用率 |
|---|---|---|---|
| Headhunter | 427 | 98 | 22.95% |
| Recruitment Platform | 465 | 105 | 22.58% |
| Campus Recruitment | 457 | 90 | 19.69% |
| Employee Referral | 474 | 91 | 19.20% |
| Official Website | 430 | 82 | 19.07% |
从结果来看:
Headhunter录用率最高,为22.95%;
Recruitment Platform录用率为22.58%,与猎头渠道非常接近;
Employee Referral和Official Website录用率相对较低;
Recruitment Platform的候选人数最多,为465人,同时录用人数也是最高的105人。
因此,如果仅从本项目数据来看,猎头渠道在录用效率方面表现较好,而招聘平台兼具较大的候选人规模和较高的录用人数。
需要注意的是,录用率并不能单独作为评价渠道优劣的唯一指标,还应结合招聘成本、岗位类型、候选人质量和招聘周期进行综合判断。
十四、招聘状态分析
接下来分析申请记录在不同招聘状态下的分布情况。
SQL代码:
SELECT application_status AS 招聘状态, COUNT(*) AS 申请人数, ROUND( COUNT(*) * 100.0 / (SELECT COUNT(*) FROM applications), 2 ) AS 占比 FROM applications GROUP BY application_status ORDER BY 申请人数 DESC;分析结果:
| 招聘状态 | 申请记录数 | 占比 |
|---|---|---|
| Applied | 526 | 17.53% |
| Rejected | 518 | 17.27% |
| Offer | 503 | 16.77% |
| Screening | 502 | 16.73% |
| Hired | 488 | 16.27% |
| Interview | 463 | 15.43% |
从整体分布来看,各招聘状态的申请记录数量比较接近。
其中 Applied 状态数量最多,为526条,占17.53%;Hired状态为488条,占16.27%。
这里需要特别说明:
由于一个候选人可能对应多条申请记录,因此这里统计的是申请记录数量,不能直接理解为488名候选人最终入职。
同时,由于不同申请记录可能处于不同状态,因此本项目不将该图直接定义为严格意义上的招聘漏斗,而是将其作为招聘流程状态分布分析。
十五、岗位申请规模与录用率分析
接下来从岗位维度进行分析。
SQL代码:
SELECT j.job_id AS 岗位编号, j.job_name AS 岗位名称, j.department AS 部门, COUNT(DISTINCT a.candidate_id) AS 申请人数, COUNT(DISTINCT CASE WHEN a.application_status = 'Hired' THEN a.candidate_id END) AS 录用人数, ROUND( COUNT(DISTINCT CASE WHEN a.application_status = 'Hired' THEN a.candidate_id END) / COUNT(DISTINCT a.candidate_id) * 100, 2 ) AS 录用率 FROM jobs j LEFT JOIN applications a ON j.job_id = a.job_id GROUP BY j.job_id, j.job_name, j.department ORDER BY 录用率 DESC;部分岗位分析结果如下:
| 岗位 | 部门 | 申请人数 | 录用人数 | 录用率 |
|---|---|---|---|---|
| J0099 产品经理 | 人力资源部 | 30 | 11 | 36.67% |
| J0097 财务专员 | 技术部 | 20 | 7 | 35.00% |
| J0027 海外运营专员 | 海外业务部 | 32 | 10 | 31.25% |
| J0080 数据分析师 | 行政部 | 26 | 8 | 30.77% |
| J0056 招聘专员 | 行政部 | 24 | 7 | 29.17% |
| J0052 产品经理 | 财务部 | 21 | 6 | 28.57% |
| J0018 海外运营专员 | 数据部 | 28 | 8 | 28.57% |
| J0100 测试工程师 | 产品部 | 35 | 9 | 25.71% |
| J0030 海外运营专员 | 财务部 | 45 | 7 | 15.56% |
| J0041 测试工程师 | 产品部 | 38 | 4 | 10.53% |
| J0083 财务专员 | 人力资源部 | 41 | 2 | 4.88% |
| J0069 行政专员 | 海外业务部 | 29 | 1 | 3.45% |
从结果可以发现:
申请人数多,并不一定意味着岗位录用率高。
例如:
J0030 海外运营专员申请人数达到45人,但录用率只有15.56%;
J0099 产品经理申请人数为30人,但录用率达到36.67%。
因此,在分析岗位招聘情况时,需要同时关注:
岗位申请规模 + 岗位录用人数 + 岗位录用率
而不能仅根据申请人数判断岗位招聘效果。
十六、部门招聘分析
进一步从部门维度分析招聘情况。
SQL代码:
SELECT j.department AS 部门, COUNT(DISTINCT j.job_id) AS 岗位数量, COUNT(DISTINCT a.candidate_id) AS 申请人数, COUNT(DISTINCT CASE WHEN a.application_status = 'Hired' THEN a.candidate_id END) AS 录用人数, ROUND( COUNT(DISTINCT CASE WHEN a.application_status = 'Hired' THEN a.candidate_id END) / COUNT(DISTINCT a.candidate_id) * 100, 2 ) AS 录用率 FROM jobs j LEFT JOIN applications a ON j.job_id = a.job_id GROUP BY j.department ORDER BY 录用率 DESC;分析结果:
| 部门 | 岗位数量 | 申请人数 | 录用人数 | 录用率 |
|---|---|---|---|---|
| 行政部 | 17 | 402 | 94 | 23.38% |
| 人力资源部 | 15 | 355 | 74 | 20.85% |
| 财务部 | 12 | 294 | 59 | 20.07% |
| 技术部 | 14 | 344 | 69 | 20.06% |
| 产品部 | 8 | 195 | 38 | 19.49% |
| 海外业务部 | 14 | 358 | 67 | 18.72% |
| 运营部 | 13 | 315 | 46 | 14.60% |
| 数据部 | 7 | 191 | 25 | 13.09% |
从结果来看:
行政部录用率最高,为23.38%;
人力资源部、财务部和技术部的录用率较为接近;
运营部和数据部的录用率相对较低;
数据部虽然岗位数量较少,但仍存在一定的招聘需求。
因此,可以进一步关注不同部门之间岗位需求和候选人匹配程度的差异。
十七、学历与录用情况分析
最后分析不同学历候选人的录用情况。
SQL代码:
SELECT c.education AS 学历, COUNT(DISTINCT c.candidate_id) AS 候选人数, COUNT(DISTINCT CASE WHEN a.application_status = 'Hired' THEN c.candidate_id END) AS 录用人数, ROUND( COUNT(DISTINCT CASE WHEN a.application_status = 'Hired' THEN c.candidate_id END) / COUNT(DISTINCT c.candidate_id) * 100, 2 ) AS 录用率 FROM candidates c LEFT JOIN applications a ON c.candidate_id = a.candidate_id GROUP BY c.education ORDER BY 录用率 DESC;分析结果:
| 学历 | 候选人数 | 录用人数 | 录用率 |
|---|---|---|---|
| 本科 | 501 | 199 | 39.72% |
| 硕士 | 499 | 191 | 38.28% |
可以看到:
本科候选人501人;
硕士候选人499人;
两类候选人的规模基本一致;
本科候选人录用率为39.72%;
硕士候选人录用率为38.28%。
两者仅相差1.44个百分点,整体差异并不明显。
因此,仅从本项目数据来看,学历对候选人录用率的影响并不明显。
但需要注意,这里计算的是基于申请数据得到的候选人录用率,并不等同于严格意义上的最终入职率。
十八、Power BI招聘数据可视化
完成MySQL数据分析后,将上述SQL分析结果整理为Excel文件,并导入Power BI进行可视化。
本项目没有直接将原始4张数据表全部导入Power BI,而是先通过MySQL完成分析,将不同分析主题的结果整理成独立的数据表,再用于Power BI展示。
最终Dashboard主要包含以下5个可视化模块:
各招聘渠道录用率
各部门录用率
岗位申请规模与录用率分析
招聘状态分布
不同学历候选人录用率
十九、最终招聘数据Dashboard
将以上分析结果整合后,最终形成招聘数据分析Dashboard。
Dashboard顶部设置项目标题:
招聘数据分析与可视化 Dashboard
中间区域重点展示招聘渠道、部门以及岗位层面的招聘情况,底部展示招聘状态和学历分析。
最终页面形成从:
招聘渠道 → 部门 → 岗位 → 招聘状态 → 候选人学历
的多维度分析结构。
二十、核心分析结论
通过对招聘数据进行SQL分析和Power BI可视化,可以得到以下几个主要结论。
1. 招聘渠道方面
Headhunter录用率最高,为22.95%;Recruitment Platform录用率为22.58%,同时拥有较大的候选人规模。
因此,在本项目数据中,这两类渠道整体表现较好。
2. 招聘状态方面
各招聘状态的申请记录数量比较接近,其中Applied数量最多,为526条,占17.53%。
由于同一候选人可能存在多条申请记录,因此不能直接将申请状态数量理解为独立候选人数。
3. 岗位方面
不同岗位之间的申请规模和录用率存在明显差异。
例如J0030海外运营专员申请人数达到45人,但录用率只有15.56%;而J0099产品经理申请人数为30人,录用率达到36.67%。
说明:
岗位申请人数多不代表招聘转化效率高。
4. 部门方面
行政部录用率最高,为23.38%;数据部和运营部录用率相对较低。
可以进一步针对低录用率部门分析岗位要求、候选人来源和岗位匹配情况。
5. 学历方面
本科候选人录用率为39.72%,硕士候选人录用率为38.28%,两者仅相差1.44个百分点。
因此,在当前数据范围内,不同学历候选人的录用率差异并不明显。
二十一、项目总结
通过本次招聘数据分析项目,我完整实践了从数据处理到数据可视化的基本流程。
项目主要使用:
MySQL:数据导入、数据清洗、数据质量检查、SQL分析
Excel:整理SQL分析结果
Power BI:数据可视化与Dashboard制作
在数据处理过程中,重点实践了:
字符集问题处理
重复值检查
缺失值检查
数值异常检查
业务规则检查
表关联完整性检查
业务重复判断
在数据分析过程中,通过GROUP BY、COUNT、COUNT(DISTINCT)、CASE WHEN、LEFT JOIN等SQL语句,从招聘渠道、招聘状态、岗位、部门和学历等多个维度进行了分析。
同时,通过Power BI将SQL分析结果转化为可视化Dashboard,使招聘数据更加直观。
通过这个项目,也进一步认识到:
数据分析不仅是编写SQL查询,更重要的是理解业务逻辑,并判断数据结果是否具有合理的业务含义。
例如,同一候选人多次申请同一岗位并不一定意味着数据重复;招聘状态数量也不能简单理解为招聘漏斗;入职日期异常也需要结合数据粒度和业务背景进行判断。
因此,在实际数据分析工作中,需要同时具备:
数据处理能力 + SQL分析能力 + 业务理解能力 + 数据可视化能力。
二十二、项目技术栈
数据库:MySQL 数据库管理工具:Navicat 数据整理:Excel 数据可视化:Power BI 核心技能: SQL数据清洗 SQL多表关联 GROUP BY 聚合函数 CASE WHEN COUNT DISTINCT LEFT JOIN 业务规则检查 招聘数据分析 Power BI Dashboard二十三、项目成果
最终完成:
4张HR核心数据表的数据导入
数据质量检查与清洗
5个招聘主题SQL分析结果
招聘渠道分析
招聘状态分析
岗位分析
部门分析
学历分析
Power BI招聘数据Dashboard
项目完整实现了:
数据准备 ↓ MySQL数据处理 ↓ SQL数据分析 ↓ Excel结果整理 ↓ Power BI可视化 ↓ 招聘数据Dashboard整个项目以招聘业务为背景,将数据库操作、SQL分析和BI可视化结合起来,形成了一套较完整的招聘数据分析流程。