招聘系统职位表需支持状态流转与高效检索:job_postings表须含status、expires_at、department_id、required_skills(JSON类型)、salary_min/max(DECIMAL),并建联合索引;job_applications表需唯一约束(job_id,candidate_id)及外键关联;用CTE实现热度统计;通过视图隔离敏感字段实现权限控制。

职位表设计要支持状态流转和快速检索
招聘系统里 job_postings 表不能只存标题和描述。必须包含 status(如 'draft'、'published'、'closed')、expires_at(避免过期职位持续展示)、department_id 和 required_skills(建议用 JSON 类型,MySQL 5.7+ 支持,比逗号分隔更易查询)。别用 TEXT 存技能列表——后续想查“哪些职位要 Python”,JSON_CONTAINS(required_skills, '"Python"') 才能走索引。
常见错误:把 salary_range 设成单个 VARCHAR 字段(如 '15K-25K'),导致无法排序或区间筛选。应拆为 salary_min 和 salary_max 两个 DECIMAL(10,2) 字段。
CREATE TABLE job_postings (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
department_id INT NOT NULL,
status ENUM('draft', 'published', 'closed') DEFAULT 'draft',
expires_at DATETIME NULL,
salary_min DECIMAL(10,2) NULL,
salary_max DECIMAL(10,2) NULL,
required_skills JSON NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_status_published (status, expires_at),
INDEX idx_dept_status (department_id, status)
);申请记录必须绑定职位和候选人,且防重复提交
job_applications 表核心是三个外键:job_id、candidate_id(指向用户表)、resume_id(指向附件表)。关键约束是 UNIQUE(job_id, candidate_id) —— 同一人对同一职位只能申请一次,数据库层兜底,比应用层判断更可靠。
容易踩的坑:没加 created_at 时间戳,导致无法按投递时间排序;或者把简历内容直接塞进该表的 resume_text 字段,造成表膨胀、备份慢、全文检索难。正确做法是只存 resume_id,简历文本/文件另存。
- 状态字段
application_status推荐用ENUM('applied', 'reviewed', 'interviewing', 'rejected', 'hired'),别用数字编码,可读性差且易错 - 如果支持“撤回申请”,更新状态即可,不要物理删除记录——否则统计报表会丢数据
- 对
job_id和candidate_id建联合索引,高频查询如“查某人所有申请”或“查某职位所有申请人”才快
用 MySQL 8.0+ 的 CTE 实现职位热度统计
运营常问:“最近7天被查看最多、申请最多的前5个职位?” 如果用子查询嵌套,SQL 又长又难维护。MySQL 8.0 支持 CTE,可以清晰拆解:
先算每个职位的查看数(来自 job_views 日志表),再算申请数(来自 job_applications),最后 JOIN 后排序。注意 LEFT JOIN,避免没申请的职位被漏掉。
WITH view_counts AS ( SELECT job_id, COUNT(*) as views FROM job_views WHERE viewed_at >= NOW() - INTERVAL 7 DAY GROUP BY job_id ), app_counts AS ( SELECT job_id, COUNT(*) as applications FROM job_applications WHERE created_at >= NOW() - INTERVAL 7 DAY GROUP BY job_id ) SELECT jp.title, COALESCE(vc.views, 0) as views_last_7d, COALESCE(ac.applications, 0) as apps_last_7d FROM job_postings jp LEFT JOIN view_counts vc ON jp.id = vc.job_id LEFT JOIN app_counts ac ON jp.id = ac.job_id WHERE jp.status = 'published' ORDER BY (COALESCE(vc.views, 0) + COALESCE(ac.applications, 0)) DESC LIMIT 5;
权限控制别全靠应用层,用 MySQL 视图隔离敏感字段
HRBP 只能看本部门职位,招聘专员能看到所有职位但不能改薪资——这类规则光靠后端 if 判断风险高。MySQL 视图能固化权限逻辑:
给招聘专员建视图 v_job_public,SELECT 时直接过滤掉 salary_min、salary_max 字段,并加 WHERE status != 'draft';给部门管理员建 v_job_dept,加 WHERE department_id = @current_dept_id(应用连接时 SET 变量)。这样即使 SQL 注入绕过后端校验,数据库返回的数据也天然受限。
关键点:视图不提升性能,反而可能变慢;所以只用于权限裁剪,别指望它优化复杂 JOIN。另外,MySQL 视图不支持参数化,@current_dept_id 这种变量依赖客户端连接时显式设置,别忘了在应用代码里 SET @current_dept_id = ?。










