SQL Server - 带日期的递归查询

时间:2014-04-29 21:45:03

标签: sql-server recursive-query

我有一个看起来像这样的数据集

id          sid     start           end
34011   10017   2006-01-11  2006-06-28
66          10017   2006-10-24  NULL
67          10017   2007-04-15  NULL
68          10017   2007-10-15  2007-12-31
71          10017   2008-04-15  NULL
72          10017   2008-10-15  NULL
69          10017   2009-04-16  NULL
70          10017   2009-10-15  2010-03-01
33022   10019   2005-09-01  2005-10-03
35425   10019   2006-03-15  2006-09-15
35555       10019   2006-10-01      NULL
79          10019   2006-12-01  2007-04-12
88          10019   2010-01-02      NULL
99          10019   2010-07-02      NULL

我需要一个查询(我认为是递归的)来提取以下内容

10017       2006-01-11        2006-06-28
10017       2006-10-24        2007-12-31
10017       2008-04-15        2010-03-01  
10019       2005-09-01        2005-10-03
10019       2006-03-15        2006-09-15
10019       2006-10-01        2007-04-12
10019       2010-01-02        NULL

查询的要点是我需要跨越多行的开始和结束范围

2 个答案:

答案 0 :(得分:0)

嗯,这不是最漂亮的代码,但这将完成工作。

我的测试表名为#results,因此您显然希望将其替换为您的真实表名。

-- First find the next endDate for any record with a null endDate 

select  [sid], startDate,
        (   select MIN(endDate) 
            from #results r 
            where r.[sid] = #results.[sid] 
              and r.endDate is not null 
              and r.endDate > #results.startDate
            group by r.[sid]
        ) as endDate
into #nullEndDates
from #results
where #results.endDate is null

-- Then union those results with records that have an endDate 
-- and get the min(startDate) for the unioned result

select [sid], min(StartDate) as startDate, endDate
from 
(       
    select [sid], min(StartDate) as startDate, endDate
    from #nullEndDates
    group by [sid], endDate

    union

    select [sid], startDate, endDate
    from #results
    where #results.endDate is not null

) results
group by [sid], endDate
order by [sid], min(startDate)

我的第一次尝试是使用LEAD()根据下一个startDate填充所有endDates。我不认为这是你想要的,但以防万一:

select  [sid], 
        startDate, 
        isnull(endDate, LEAD(startDate) OVER (ORDER BY [sid])) as endDate
from #results
order by [sid], startDate

答案 1 :(得分:0)

尝试下面的sql:

这是概念:

  1. 修复id值,此时它很乱。
  2. 创建一个映射表以帮助我们获得结果。
  3. 注意:请重新检查sql(表名,字段名等)

    -- Declare TempData Table
    declare @tempData table
    (
      id int identity,
      [sid] int,
      start date,
      [end] date
    );
    -- Declare TempMapping Table
    declare @tempMapping table
    (
      id int identity,
      start_id int,
      end_id int
    );
    
    --Insert the data to @tempData, with new ID
    insert into @tempData([sid],[start],[end])
    select [sid],[start],[end] from @temp 
    order by [sid],[start],[end]
    ;
    
    --insert The Mapping value
    insert into @tempMapping(start_id,end_id)
    select 
        start_id , end_id
    from
    (
        select start_id, end_id
        , ROW_NUMBER() over ( partition by end_id order by start_id) as xxrow
        from
        (
            select 
            a.id as start_id ,bb.id as end_id
            ,ROW_NUMBER() over(partition by a.id order by a.id) as xrow
            from 
            (
                 select b.id, b.[sid],b.[start]
                    from @tempData b
                    where b.[end] is null
            ) a
            left join
            (
                select b.id, b.[sid],b.[start],b.[end]
                from @tempData b
                where b.[end] is not null
            ) bb on a.[sid] = bb.[sid] and a.[id]< bb.id
    
            group by 
            a.id, a.[sid],a.[start]
            ,bb.[id]
        ) c
        where c.xrow = 1
    ) d
    where d.xxrow =1 
    order by start_id, end_id
    ;
    /*
    select * from @tempMapping
    --Table Mapping Result:
    id          start_id    end_id
    ----------- ----------- -----------
    1           2           4
    2           5           8
    3           11          12
    4           13          NULL
    
    */
    
    
    --Result Query
    select 
        e.[id],e.[sid], e.[start],e.[end]
    from @tempData e
    left join @tempMapping f on f.end_id = e.id
    where e.[end] is not null and f.id is null
    union
    select 
        start_id as id, b.sid, b.start, c.[end]
    from @tempMapping a
    left join @tempData b on a.start_id = b.id
    left join @tempData c on a.end_id = c.id
    

    结果表:

    id          sid         start      end
    ----------- ----------- ---------- ----------
    1           10017       2006-01-11 2006-06-28
    2           10017       2006-10-24 2007-12-31
    5           10017       2008-04-15 2010-03-01
    9           10019       2005-09-01 2005-10-03
    10          10019       2006-03-15 2006-09-15
    11          10019       2006-10-01 2007-04-12
    13          10019       2010-01-02 NULL