
本文介绍一种简洁可靠的 sql 方法,利用 not in 子查询联动 available_week(周可用性)和 available_day(日异常状态)两张表,排除休假或病假员工,准确返回某日某时段仍处于正常工作状态的员工编号。
本文介绍一种简洁可靠的 sql 方法,利用 not in 子查询联动 available_week(周可用性)和 available_day(日异常状态)两张表,排除休假或病假员工,准确返回某日某时段仍处于正常工作状态的员工编号。
在实际考勤或排班系统中,员工的常规工作安排通常按周定义(如“周一 8:00–17:00 正常出勤”),而临时变动(如请假、病假、调休)则按日记录。这种分离式设计提升了数据灵活性,但也给实时查询带来挑战:仅查周表会遗漏当日异常;仅查日表又无法覆盖未显式记录的“默认可用”员工。
要解决这一问题,核心逻辑是:
✅ 先从 available_week 中获取所有本应于目标时段(如“周一 8 点”)工作的员工;
❌ 再从中剔除那些在 available_day 中被明确标记为不可用(state = 0)的员工——且该记录的日期需严格匹配查询日。
以下为推荐的 SQL 实现(兼容 MySQL、PostgreSQL、SQL Server 等主流数据库):
SELECT short
FROM available_week
WHERE day = 1 AND hour = 8
AND short NOT IN (
SELECT short
FROM available_day
WHERE state = 0
AND day = '2022-04-09'
);? 关键说明:
- day = 1 表示星期一(按 ISO 标准,1=Monday;请根据实际数据校验取值逻辑);
- '2022-04-09' 是目标查询日期,必须与 available_day.day 字段格式一致(示例中为 YYYY-MM-DD 字符串,若字段为 DATE 类型则无需引号);
- 使用 NOT IN 而非 LEFT JOIN ... IS NULL,语义更直观,性能在中小数据量下表现良好;但需注意:若子查询返回 NULL 值,NOT IN 整体结果为空(这是 SQL 三值逻辑特性),因此务必确保 available_day.short 列非空,或改用 NOT EXISTS 做更健壮处理(见进阶建议)。
✅ 正确执行前提:
- 两张表的 short 字段类型与字符集完全一致(避免隐式转换失败);
- available_day 中仅记录异常状态(state = 0),正常日不插入记录(即“无记录 = 默认可用”);
- 时间粒度对齐:available_week.hour 应代表开始小时(如 8 表示 08:00–08:59),且 available_day.time 含义一致。
⚠️ 进阶建议(高可靠性场景):
若数据量大或存在 NULL 风险,推荐使用 NOT EXISTS 替代 NOT IN:
SELECT w.short
FROM available_week w
WHERE w.day = 1 AND w.hour = 8
AND NOT EXISTS (
SELECT 1
FROM available_day d
WHERE d.short = w.short
AND d.state = 0
AND d.day = '2022-04-09'
);该写法不受 NULL 影响,且可借助 (short, day) 复合索引显著提升性能。
综上,此方案以最小改动实现跨表状态融合,兼顾可读性、可维护性与生产可用性,是处理“默认规则 + 日级例外”类业务查询的经典范式。










