【问题标题】:Trying to turn daily info into date ranges in one row试图将每日信息连续转换为日期范围
【发布时间】:2021-09-30 16:50:02
【问题描述】:

我有一个每天都会更新的表,即使没有数据发生变化。感兴趣的列是生效日期、ID、状态。我想要日期范围,而不是每天都有记录。 ID、状态、开始日期、结束日期。

我尝试使用 Dense_Rank Over (Partition by Id, Status order by Effective Date) 创建一个具有 Dense Rank 的表。然后我打算使用密集排名来获取生效日期的最小值和最大值,但排名并没有像我想象的那样出来:

Date.      ID.    Status.  Rownum.
10/01/2021 Abc123 InActive 1
10/02/2021 Abc123 InActive 1
10/03/2021 Abc123 Active 2
10/04/2021 Abc123 Active 2
10/01/2021 Def456 InActive 1
10/02/2021 Def456 InActive 1
10/03/2021 Def456 Active 2
10/04/2021 Def456 active 2

但事实并非如此。寻找有关如何执行此操作的想法。 编辑:

最后,我想要一个像这样的表:

身份证。地位。开始日期。结束日期

Abc123 无效。 2021 年 10 月 1 日 2021 年 10 月 2 日

Anc123 激活。 2021 年 10 月 3 日 2021 年 10 月 4 日

【问题讨论】:

  • 该数据是您的输入、当前输出还是想要的输出?
  • 这就是我想要达到的目标。前 3 列只是直接从表中取出的数据。最后一栏是我想要完成的。

标签: sql hiveql


【解决方案1】:

这是一个线性分割的问题。在您的情况下,您的线性度量是时间。这是我在 SQL Server 中执行此操作的方法。你的旅费可能会改变。我添加了一些额外的输入来演示处理一些极端情况。

declare @MyTable table (
    [Date.] date not null,
    [ID.] varchar(6) not null,
    [Status.] varchar(10) not null
)

insert @MyTable
values 
  ({d '2021-10-01'}, 'Abc123', 'InActive')
, ({d '2021-10-02'}, 'Abc123', 'InActive')
, ({d '2021-10-03'}, 'Abc123', 'Active')
, ({d '2021-10-04'}, 'Abc123', 'Active')
, ({d '2021-10-05'}, 'Abc123', 'Active')
, ({d '2021-10-06'}, 'Abc123', 'InActive')
, ({d '2021-10-07'}, 'Abc123', 'Active')
, ({d '2021-10-08'}, 'Abc123', 'InActive')
, ({d '2021-10-09'}, 'Abc123', 'InActive')
, ({d '2021-10-01'}, 'Def456', 'InActive')
, ({d '2021-10-02'}, 'Def456', 'InActive')
, ({d '2021-10-03'}, 'Def456', 'Active')
, ({d '2021-10-04'}, 'Def456', 'Active')

;
with a as (
    select lag([ID.]) over(order by [ID.], [Date.]) as IDPrior
    , [ID.]
    , lead([ID.]) over(order by [ID.], [Date.]) as IDNext
    , lag([Status.]) over (order by [ID.], [Date.]) as StatusPrior
    , [Status.]
    , lead([Status.]) over (order by [ID.], [Date.]) as StatusNext
    , [Date.]
    , row_number() over (order by [ID.], [Date.]) as 'RowNum'

    from @MyTable
)
, b as (
    select *
    , CASE
        WHEN RowNum = 1
            THEN 'BEGIN'
        WHEN IDPrior <> [ID.]
            THEN 'BEGIN'
        WHEN StatusPrior <> [Status.]
            THEN 'BEGIN'
        ELSE 'Continue'
      END as SegmentBegin
    , CASE
        WHEN [ID.] <> IDNext
            THEN 'END'
        WHEN [Status.] <> StatusNext
            THEN 'END'
        WHEN RowNum = max(rownum) over ()
            THEN 'END'
        ELSE 'Continue'
      END as SegmentEnd
    
    from a
)
, c as (
    select [ID.]
    , [Status.]
    , [Date.]
    , case when SegmentBegin = 'BEGIN' then [Date.] end as BeginDate
    , case when SegmentEnd   = 'END'   then [Date.] end as EndDate

    from b

    where SegmentBegin = 'BEGIN'
       or SegmentEnd = 'END'
)
, d as (
    select [ID.]
    , [Status.]
    , [Date.]
    , BeginDate
    , case when EndDate is null and lead([ID.]) over (order by [ID.], [Date.]) = [ID.] then lead(EndDate) over (order by [ID.], [Date.]) else EndDate end as EndDate
    
    from c
)
, e as (
    select [ID.]
    , [Status.]
    , [Date.]
    , BeginDate
    , case when EndDate is null then lead(EndDate) over (order by [ID.], [Date.]) else EndDate end as EndDate
    
    from d
)

select [ID.]
, [Status.]
, BeginDate as 'BeginDate.'
, EndDate

from e
    
where BeginDate is not null
order by [ID.]
, [Date.]

输出:

ID. Status. BeginDate. ENDDate
Abc123 InActive 2021-10-01 2021-10-02
Abc123 Active 2021-10-03 2021-10-05
Abc123 InActive 2021-10-06 2021-10-06
Abc123 Active 2021-10-07 2021-10-07
Abc123 InActive 2021-10-08 2021-10-09
Def456 InActive 2021-10-01 2021-10-02
Def456 Active 2021-10-03 2021-10-04

【讨论】:

  • 对不起,状态可以重复是为什么我需要行号来分隔相同状态的不同实例。所以有人可能会多次活跃和不活跃。
  • 这就是为什么您的问题应该包含足够的示例数据来处理边缘情况。
猜你喜欢
  • 1970-01-01
  • 2021-07-20
  • 1970-01-01
  • 2013-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-18
相关资源
最近更新 更多