考勤表必须包含 attendance_id
、employee_id
、check_in
、check_out
四个核心字段
缺一不可,否则无法支持“打卡时间计算”和“员工维度统计”。
check_in和
check_out必须用
DATETIME类型(不是
DATE或
TIMESTAMP),否则午休后二次打卡、跨天加班等场景会出错。
employee_id要设为外键关联员工表,避免脏数据;
attendance_id建议设为自增主键,不推荐用 UUID——插入性能差,且 MySQL 8.0 以前对 UUID 排序效率极低。
员工表要预留 status
字段并设默认值 'active'
实际运维中常遇到员工离职但考勤记录需保留的情况。如果删员工记录,历史考勤就变成“幽灵打卡”;如果只停用不删,没
status字段就只能靠注释或额外表标记,查起来麻烦又易错。
status类型用
ENUM('active', 'inactive', 'on_leave') 比 VARCHAR更安全,防止拼写错误 加索引:
CREATE INDEX idx_employee_status ON employee(status);查询当月在职人员考勤时,直接
WHERE e.status = 'active',不用连表过滤或写子查询
check_in
和 check_out
允许 NULL,但业务层必须控制只有一方可为空
早退、忘打卡、设备故障都会导致单边缺失。数据库层面不能强制非空,否则录入失败;但也不能放任两边都空,那这条记录就失去意义。
建表时定义:check_in DATETIME NULL, check_out DATETIME NULL用触发器或应用逻辑校验:不允许
check_in IS NULL AND check_out IS NULL计算工时别直接写
TIMESTAMPDIFF,先加判断:
IF(check_in IS NOT NULL AND check_out IS NOT NULL, TIMESTAMPDIFF(HOUR, check_in, check_out), NULL)
别在考勤表里存“是否迟到”这类衍生字段
迟到规则常变(比如弹性15分钟、节假日调整、部门差异化),硬编码进字段会导致每次改规则都要
ALTER TABLE+ 全量更新,风险高、耗时长。
正确做法是用视图或查询时实时算:
SELECT *,
CASE
WHEN TIME(check_in) > '09:15:00' THEN 'late'
ELSE 'on_time'
END AS attendance_status
FROM attendance a
JOIN employee e ON a.employee_id = e.employee_id;
复杂规则(如按部门设置不同打卡时间)就用关联配置表,而不是塞进主表字段里。
实际部署时最容易被忽略的是时区处理:MySQL 服务端、连接客户端、应用代码三者时区不一致,会导致NOW()插入的时间比本地晚/早几小时。上线前务必统一设为
SYSTEM或明确指定
+08:00,别依赖默认值。
