在每个 id 的空值之后查找第一次出现

Finding the first occurrence after a null value for each id

有两个表:A) 时间范围内的所有有效日期 B) 事件 ID 列表和事件发生的相应日期。

dt_util
2022-05-01
2022-05-02
2022-05-04
2022-05-05
2022-05-06
2022-05-07
id_event dt_event
1 2022-05-01
2 2022-05-01
3 2022-05-01
1 2022-05-02
3 2022-05-02
1 2022-05-04
2 2022-05-04
3 2022-05-04
1 2022-05-05
2 2022-05-05
1 2022-05-07
2 2022-05-07

期望的结果是为每个 ID 找到最后一个“缺失”(NULL) 日期之后的最小日期,如下所示:

id_event min_dt_event
1 2022-05-07
2 2022-05-07
3 2022-05-01

我一直在尝试使用 FIRST_VALUE 和 ROW_NUMBER,但到目前为止运气不好。我知道这两个表可能都经过一个循环,但是对于真实的数据集来说这是不可行的。

    -- Creating temp tables
    DROP TABLE IF EXISTS #TEMP_EVENTS
    DROP TABLE IF EXISTS #TEMP_DATES
    CREATE TABLE #TEMP_EVENTS (
        id_event smallint,
        dt_event date
    )
    
    CREATE TABLE #TEMP_DATES (
        dt_util date
    )

    INSERT INTO #TEMP_EVENTS VALUES 
            (1, '2022-05-01'),
            (2, '2022-05-01'),
            (3, '2022-05-01'),
            (1, '2022-05-02'),
            (3, '2022-05-02'),
            (1, '2022-05-04'),
            (2, '2022-05-04'),
            (3, '2022-05-04'),
            (1, '2022-05-05'),
            (2, '2022-05-05'),
            (1, '2022-05-07'),
            (2, '2022-05-07')
            
    INSERT INTO #TEMP_DATES VALUES 
            ('2022-05-01'),
            ('2022-05-02'),
            ('2022-05-04'),
            ('2022-05-05'),
            ('2022-05-06'),
            ('2022-05-07')

    -- Creating fictional table where all ids matches all dates
    DROP TABLE IF EXISTS #TEMP_ID_EVENTS
    SELECT DISTINCT ID_EVENT, DT_UTIL 
    INTO #TEMP_ALL_DATES 
    FROM #TEMP_EVENTS A 
    LEFT JOIN #TEMP_DATES B
    ON 1 = 1
    
    -- Minimum date after a null date
    SELECT 
         A.ID_EVENT, [DT_UTIL], [DT_EVENT],
         FIRST_VALUE([DT_UTIL]) OVER (PARTITION BY A.[ID_EVENT] ORDER BY [DT_EVENT] DESC) AS MIN_DATE
    FROM 
        #TEMP_ALL_DATES A
    LEFT JOIN 
        #TEMP_EVENTS B
    ON  
        A.id_event = B.id_event
    AND A.dt_util  = B.dt_event
    WHERE dt_event IS NULL

sub-query 使用 row_number()#TEMP_DATES 中的日期生成 运行 数字。

然后加入 date 上的那个 sub-query 并使用 lag() 检查 row_number rn,如果没有任何缺失,它应该与 1 不同日期。

最后只需获取 max 即可获取“最后缺失的空值”的日期

with cte as
(
    select *,
           flag = case when d.rn <> 1 
                       and  lag(d.rn) over(partition by e.id_event 
                                               order by e.dt_event) <> d.rn - 1
                       then 1
                       else 0
                       end
    from   #TEMP_EVENTS e
           inner join
           (
               select rn = row_number() over (order by dt_util),
                      dt_util
               from   #TEMP_DATES
           ) d             on e.dt_event = d.dt_util
)
select id_event, 
       dt_event = max(case when flag = 1 then dt_event end)
from   cte
group by id_event

结果:

id_event dt_event
1 2022-05-07
2 2022-05-07
3 null

db<>fiddle demo