☰
基于 MySQL + Power BI 的招聘数据分析与可视化项目实战
2026/10/8 4:09:10 网站建设 项目流程

一、项目背景

随着企业招聘规模不断扩大,招聘过程中会产生大量候选人、岗位、申请记录以及员工入职等数据。

通过对招聘数据进行系统分析,可以从招聘渠道、招聘状态、岗位、部门、学历等多个维度了解企业招聘情况,为招聘渠道优化、岗位需求调整以及招聘效率提升提供数据支持。

本项目以一组模拟企业招聘数据为基础,使用MySQL完成数据导入、数据清洗与数据分析,并使用Power BI对分析结果进行可视化展示,最终制作招聘数据分析 Dashboard。

项目整体流程如下:

原始CSV数据 ↓ MySQL数据导入 ↓ 数据质量检查 ↓ 数据清洗 ↓ SQL多维度分析 ↓ Excel整理分析结果 ↓ Power BI可视化 ↓ 招聘数据分析Dashboard

二、项目目标

本项目主要希望解决以下几个问题:

  1. 不同招聘渠道的候选人数和录用率有什么差异?

  2. 当前招聘流程中,各种招聘状态的申请记录如何分布?

  3. 哪些岗位申请人数较多?哪些岗位录用率较高?

  4. 不同部门的招聘表现有什么差异?

  5. 不同学历候选人的录用情况是否存在明显差异?

  6. 如何通过 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;

分析结果:

招聘渠道候选人数录用人数录用率
Headhunter4279822.95%
Recruitment Platform46510522.58%
Campus Recruitment4579019.69%
Employee Referral4749119.20%
Official Website4308219.07%

从结果来看:

  1. Headhunter录用率最高,为22.95%;

  2. Recruitment Platform录用率为22.58%,与猎头渠道非常接近;

  3. Employee Referral和Official Website录用率相对较低;

  4. 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;

分析结果:

招聘状态申请记录数占比
Applied52617.53%
Rejected51817.27%
Offer50316.77%
Screening50216.73%
Hired48816.27%
Interview46315.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 产品经理人力资源部301136.67%
J0097 财务专员技术部20735.00%
J0027 海外运营专员海外业务部321031.25%
J0080 数据分析师行政部26830.77%
J0056 招聘专员行政部24729.17%
J0052 产品经理财务部21628.57%
J0018 海外运营专员数据部28828.57%
J0100 测试工程师产品部35925.71%
J0030 海外运营专员财务部45715.56%
J0041 测试工程师产品部38410.53%
J0083 财务专员人力资源部4124.88%
J0069 行政专员海外业务部2913.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;

分析结果:

部门岗位数量申请人数录用人数录用率
行政部174029423.38%
人力资源部153557420.85%
财务部122945920.07%
技术部143446920.06%
产品部81953819.49%
海外业务部143586718.72%
运营部133154614.60%
数据部71912513.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;

分析结果:

学历候选人数录用人数录用率
本科50119939.72%
硕士49919138.28%

可以看到:

  • 本科候选人501人;

  • 硕士候选人499人;

  • 两类候选人的规模基本一致;

  • 本科候选人录用率为39.72%;

  • 硕士候选人录用率为38.28%。

两者仅相差1.44个百分点,整体差异并不明显。

因此,仅从本项目数据来看,学历对候选人录用率的影响并不明显。

但需要注意,这里计算的是基于申请数据得到的候选人录用率,并不等同于严格意义上的最终入职率。


十八、Power BI招聘数据可视化

完成MySQL数据分析后,将上述SQL分析结果整理为Excel文件,并导入Power BI进行可视化。

本项目没有直接将原始4张数据表全部导入Power BI,而是先通过MySQL完成分析,将不同分析主题的结果整理成独立的数据表,再用于Power BI展示。

最终Dashboard主要包含以下5个可视化模块:

  1. 各招聘渠道录用率

  2. 各部门录用率

  3. 岗位申请规模与录用率分析

  4. 招聘状态分布

  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可视化结合起来,形成了一套较完整的招聘数据分析流程。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询