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

如何在SQL Server中用CTE获取Type=NA行前置日期并处理记录

在SQL Server中用CTE获取Type=NA的前置行日期(按规则匹配结束日期)

需求规则

  • 为每个非NA类型的记录,匹配其后续最早的Type=NA记录日期作为结束日期
  • 有效记录(非NA)和对应NA行之间的其他非NA记录直接忽略,NA行仍作为该有效记录的结束日期
  • 同一人员的有效记录,需在前一个周期的NA行出现后,才开启新周期
  • 无对应NA行的有效记录,结束日期为NULL
  • 开头的NA记录直接忽略,不参与任何匹配

源数据

Person    Type     dt_eff
123       A        2018-10-23 -- 周期起始记录
123       NA       2018-11-19 -- 对应起始记录的结束日期,不输出
123       NA       2018-12-25 -- 忽略,不输出
124       A        2020-01-01 -- 周期起始记录
124       B        2020-02-15 -- 忽略,不输出
124       NA       2020-05-14 -- 对应起始记录的结束日期,不输出
124       C        2020-10-13 -- 新周期起始记录
124       NA       2021-01-15 -- 对应此起始记录的结束日期
124       A        2021-05-22 -- 新周期起始记录
124       T        2021-08-22 -- 忽略,不输出
456       NA       2022-04-19 -- 忽略,无前置有效记录
456       A        2022-05-01 -- 周期起始记录,无对应NA行,结束日期为NULL
456       B        2022-07-15 -- 忽略,不输出

预期输出

Person    Type     dt_start     dt_end
123       A        2018-10-23   2018-11-19
124       A        2020-01-01   2020-05-14
124       C        2020-10-13   2021-01-15
124       A        2021-05-22   NULL
456       A        2022-05-01   NULL

源数据DDL&DML

CREATE TABLE Person (
  Person INTEGER,
  Type VARCHAR(3),
  dt_eff Date
);

INSERT INTO Person (Person, Type, dt_eff)
VALUES
(123,'A','2018-10-23'),
(123,'NA','2018-11-19'),
(123,'NA','2018-12-25'),
(124,'A','2020-01-01'),
(124,'B','2020-02-15'),
(124,'NA','2020-05-14'),
(124,'C','2020-10-13'),
(124,'NA','2021-01-15'),
(124,'A','2021-05-22'),
(124,'T','2021-08-22'),
(456,'NA','2022-04-19'),
(456,'A','2022-05-01'),
(456,'B','2022-07-15')

你的尝试代码问题分析

原代码的分组逻辑仅在Type='NA'且与前一行类型不同时才累加分组编号,无法正确划分"有效起始记录到第一个NA行"的完整周期,导致中间的非NA记录干扰分组,无法准确匹配对应结束日期。


正确CTE解法

WITH PeriodGroups AS (
    -- 第一步:为每个记录标记所属周期分组
    SELECT 
        Person,
        Type,
        dt_eff,
        -- 累计求和:每遇到NA行就加1,同一周期内的记录归为同一组
        SUM(CASE WHEN Type = 'NA' THEN 1 ELSE 0 END) OVER (
            PARTITION BY Person 
            ORDER BY dt_eff 
            ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
        ) AS GroupId
    FROM Person
),
CycleStarts AS (
    -- 第二步:提取每个周期的起始记录,匹配对应的结束日期
    SELECT 
        Person,
        -- 取周期内第一个非NA的类型
        FIRST_VALUE(Type) OVER (
            PARTITION BY Person, GroupId 
            ORDER BY dt_eff 
            ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
        ) AS CycleType,
        -- 取周期内第一个非NA的日期作为起始日期
        FIRST_VALUE(dt_eff) OVER (
            PARTITION BY Person, GroupId 
            ORDER BY dt_eff 
            ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
        ) AS dt_start,
        -- 取周期内最早的NA日期作为结束日期,无NA则为NULL
        MIN(CASE WHEN Type = 'NA' THEN dt_eff END) OVER (
            PARTITION BY Person, GroupId 
            ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
        ) AS dt_end
    FROM PeriodGroups
    WHERE Type <> 'NA' -- 仅关注非NA记录
)
-- 第三步:去重,每个周期保留一条起始记录
SELECT DISTINCT
    Person,
    CycleType AS Type,
    dt_start,
    dt_end
FROM CycleStarts
WHERE CycleType IS NOT NULL -- 排除无有效起始记录的分组
ORDER BY Person, dt_start;

代码逻辑说明

  1. PeriodGroups:通过累计NA行数量划分周期,每出现一个NA就开启新周期,确保同一周期内的所有记录(含中间非NA)归属同一组。
  2. CycleStarts:在每个周期内,提取最早的非NA记录作为周期起点,同时匹配该周期内最早的NA日期作为结束日期。
  3. 最后去重并过滤无效分组,得到符合要求的结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 22:41:07