You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用单条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;

逻辑拆解(为什么这么写)

  1. 窗口函数关联匹配记录:

    • LEAD(timestamp):给每条进入记录(inout=1)抓取后续最早的离开时间
    • LAG(timestamp):给每条离开记录(inout=0)抓取前面最晚的进入时间
      这一步解决了"谁对应谁"的核心问题,不管是同天还是跨天都能精准匹配。
  2. 标记目标日期的关联记录:
    我们要的是和2018-04-02相关的所有匹配对,所以不仅要当天的进出,还要覆盖跨天关联的记录:

    • 前一天进入、当天离开的进入记录
    • 当天进入、次日出的进入记录
    • 当天离开、前一天进入的离开记录
      用target_flag把这些记录统一标记,避免漏掉特殊场景。
  3. 去重与格式化:

    • DISTINCT去掉重复的匹配对(比如一条完整的进出会被进入记录和离开记录各匹配一次,必须去重)
    • COALESCE把NULL值(比如John没有对应的离开记录)替换为'-',完全符合你要求的输出格式
    • 排序规则保证同姓名按进入时间排序,没有对应进入时间的离开记录(比如Anna那条单独的离开)排在前面

测试结果验证

执行这条SQL后,输出会完全匹配你的期望:

  • John的离开时间显示为'-'
  • Anna会生成两条记录:一条只有离开时间,一条完整的进出匹配对
  • Mary的两条跨天夜班记录都会被正确识别并输出

内容的提问来源于stack exchange,提问作者WoT

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:06:18