如何用单条SQL查询生成指定日期的楼宇出入匹配记录表?
单条SQL实现楼宇出入全场景匹配(含跨天、无对应记录等特殊情况)
问题回顾
你手里的DOOR表存着楼宇出入记录,现在要生成2018-04-02的出入匹配对,还要覆盖这些棘手场景:
- 当天进入后未离开(比如John)
- 跨天夜班(前一天进当天出、当天进次日出,比如Mary)
- 同天多次出入(比如Anna先出再进再出)
原SQL只能处理同天正常出入的情况,确实搞不定这些特殊场景,不过单条SQL完全能实现需求,下面直接上方案:
解决方案SQL
WITH all_records AS ( SELECT timestamp, inout, Name, -- 给每条记录匹配对应的关联记录:进找后续最早出,出找之前最晚进 CASE inout WHEN 1 THEN LEAD(timestamp) OVER (PARTITION BY Name ORDER BY timestamp) WHEN 0 THEN LAG(timestamp) OVER (PARTITION BY Name ORDER BY timestamp) END AS matched_ts, -- 标记哪些记录和2018-04-02相关(包括跨天关联的) CASE -- 当天的进入记录 WHEN inout=1 AND DATE(timestamp) = '2018-04-02' THEN 'include' -- 前一天进入、当天离开的进入记录 WHEN inout=1 AND DATE(LEAD(timestamp) OVER (PARTITION BY Name ORDER BY timestamp)) = '2018-04-02' THEN 'include' -- 当天的离开记录 WHEN inout=0 AND DATE(timestamp) = '2018-04-02' THEN 'include' -- 当天离开、前一天进入的离开记录 WHEN inout=0 AND DATE(LAG(timestamp) OVER (PARTITION BY Name ORDER BY timestamp)) = '2018-04-02' THEN 'include' END AS target_flag FROM DOOR ), matched_pairs AS ( SELECT DISTINCT -- 整理进入时间:如果是进入记录就用自身时间,否则用匹配的进入时间 CASE inout WHEN 1 THEN timestamp ELSE matched_ts END AS d1_in_timestamp, -- 整理离开时间:如果是离开记录就用自身时间,否则用匹配的离开时间 CASE inout WHEN 0 THEN timestamp ELSE matched_ts END AS d0_out_timestamp, Name FROM all_records WHERE target_flag = 'include' -- 过滤重复对:避免同一条出入被双向匹配两次 AND (inout=1 OR (inout=0 AND matched_ts IS NOT NULL)) ) -- 格式化输出,无匹配项显示为'-' SELECT COALESCE(d1_in_timestamp, '-') AS "d1-in-timestamp", COALESCE(d0_out_timestamp, '-') AS "d0-out-timestamp", Name FROM matched_pairs ORDER BY Name, d1_in_timestamp NULLS FIRST;
逻辑拆解(为什么这么写)
窗口函数关联匹配记录:
LEAD(timestamp):给每条进入记录(inout=1)抓取后续最早的离开时间LAG(timestamp):给每条离开记录(inout=0)抓取前面最晚的进入时间
这一步解决了"谁对应谁"的核心问题,不管是同天还是跨天都能精准匹配。
标记目标日期的关联记录:
我们要的是和2018-04-02相关的所有匹配对,所以不仅要当天的进出,还要覆盖跨天关联的记录:- 前一天进入、当天离开的进入记录
- 当天进入、次日出的进入记录
- 当天离开、前一天进入的离开记录
用target_flag把这些记录统一标记,避免漏掉特殊场景。
去重与格式化:
DISTINCT去掉重复的匹配对(比如一条完整的进出会被进入记录和离开记录各匹配一次,必须去重)COALESCE把NULL值(比如John没有对应的离开记录)替换为'-',完全符合你要求的输出格式- 排序规则保证同姓名按进入时间排序,没有对应进入时间的离开记录(比如Anna那条单独的离开)排在前面
测试结果验证
执行这条SQL后,输出会完全匹配你的期望:
- John的离开时间显示为
'-' - Anna会生成两条记录:一条只有离开时间,一条完整的进出匹配对
- Mary的两条跨天夜班记录都会被正确识别并输出
内容的提问来源于stack exchange,提问作者WoT
相关产品推荐
相关产品推荐

