应统一使用datetime2声明时间参数,避免datetime导致索引失效、精度丢失和隐式转换;字符串入参须显式try_convert转换;查询条件避免函数包裹索引列,改用范围查询。

SQL Server存储过程里别用datetime声明时间参数
直接用datetime2,否则索引失效、精度丢失、跨版本隐式转换全找上门。你传GETDATE()进去,datetime会截断到3.33ms,还可能把datetime2列整个转成datetime再比——全表扫描就等着你。
- 统一用
@start_date datetime2(3)或@end_date datetime2(7),和业务表字段精度对齐 - 字符串入参必须显式转换:
SET @start_date = TRY_CONVERT(datetime2(3), @date_str),失败就RETURN,不靠隐式行为兜底 - 别写
WHERE CONVERT(date, check_time) = '2024-06-09'——函数包裹索引列,查10万行变秒级延迟 - 正确写法是:
check_time >= '2024-06-09' AND check_time (注意用<code>,不是<code>)
在存储过程中用DATEADD和DATEDIFF做动态班次边界计算
考勤不是固定8点到18点,夜班可能跨天、调休日要单独算、迟到早退阈值随班次浮动——硬写死时间字符串等于埋雷。得用函数动态算。
-
DATEADD(hour, -1, @shift_start)算打卡宽限期,比拼字符串安全得多 - 判断是否迟到:用
DATEDIFF(minute, @scheduled_start, a.check_time) > 15,别用CONVERT(time, a.check_time) > '08:15'——后者漏掉跨天打卡 - 夜班结束时间若为次日6:00,别存字符串
'06:00',而应存起始时间+8小时:DATEADD(hour, 8, @night_start) - 所有时间差计算统一用
minute或second单位,避免day级计算在跨月时出错
用LAG/LEAD在存储过程里识别有效打卡对
存储过程里不能只靠MIN/MAX取上下班时间——员工上午补卡、午休刷门禁、下班前又打一次,这些都会污染结果。必须先用窗口函数清洗序列。
- 排序必须用
ORDER BY check_time,不是id或插入顺序;PARTITION BY emp_id, CAST(check_time AS DATE)是底线 - 先标记每条记录类型:
CASE WHEN CONVERT(time, check_time) BETWEEN '08:00' AND '09:00' THEN 'in'...,再用LAG(type)看上一条是不是'in'来筛重复 - 配对逻辑别写死“第一条是in、第二条是out”,要用
ROW_NUMBER() OVER (PARTITION BY emp_id, work_date ORDER BY check_time)编号后按奇偶分组 - MySQL 8.0+、SQL Server 2012+支持,SQLite不支持——本地开发别用SQLite测试再上线报错
JOIN日历表而非硬编码节假日逻辑
国务院每年发调休通知,硬写CASE WHEN date IN ('2024-02-10', ...)等于每月手动改SQL。存储过程里必须把日期逻辑外置。
- 建
calendar表,字段至少含date、is_workday、holiday_name,每年初批量更新 - 查询时
LEFT JOIN calendar c ON a.check_date = c.date,别用DATE(a.check_in_time)这种函数包裹字段 -
GROUP BY c.is_workday, c.holiday_name,漏掉c.holiday_name会导致春节和国庆全合并成一类 - 凌晨打卡(如23:59)算次日出勤?那就得在存储过程里提前把
check_time映射到work_date,而不是依赖原始时间字段
LAG就卡住。先用日期范围缩表,再进窗口逻辑,顺序不能反。











