【问题标题】:Splitting data into time intervals according to changes (T-SQL)根据变化将数据拆分为时间间隔(T-SQL)
【发布时间】:2018-04-20 21:51:40
【问题描述】:

假设我有两张桌子:

薪水

PersonId, StartDate, EndDate, Salary

标题

PersonId, StartDate, EndDate, Title

一个人的薪水与他的头衔无关,可能随时变化。

我怎样才能最好地获得工资和头衔不变的所有连续 StartDate/EndDate 时间间隔?

所以这个...

薪水

Me | 2017-01-01 | 2017-01-31 | 2000
Me | 2017-02-01 | 2017-05-31 | 2100
Me | 2017-06-01 | 2017-07-31 | 2300

标题

Me | 2017-01-01 | 2017-03-31 | Junior
Me | 2017-04-01 | 2017-07-31 | Senior

会返回:

SalaryAndTitle

Me | 2017-01-01 | 2017-01-31 | 2000 | Junior
Me | 2017-02-01 | 2017-03-31 | 2100 | Junior
Me | 2017-04-01 | 2017-05-31 | 2100 | Senior
Me | 2017-06-01 | 2017-07-31 | 2300 | Senior

这是一个简化的示例。在我的真实情况下,会有许多变化的列,并且结果数据集仍应包含时间间隔,其中这些列相对于该时间段具有未更改的值。

我正在考虑over( partition by...),但我无法让它发挥作用。任何帮助表示赞赏。

干杯,

【问题讨论】:

    标签: sql-server tsql date-range


    【解决方案1】:

    我不知道您的性能要求,但我相信会有更好的方法来做到这一点......

    解决这些问题的一种解决方案是逐日分解,然后使用标准聚合函数,例如下面我假设您有一个名为 dates 的表,其中包含您感兴趣的所有日期:

    select 
        p.personid
        ,min(ds.dt) as from 
        ,max(ds.dt) as to
        ,s.salary
        ,t.title
    from
        dates as ds
        cross join 
            (select distinct personid from salary) as p
        left outer join salary as s
            on ds.dt >= s.startdate
            and ds.dt <= s.enddate
            and p.personid = s.personid
        left outer join title as t
            on ds.dt >= t.startdate
            and ds.dt <= t.enddate
            and p.personid = t.personid
    group by
        p.personid
        ,s.salary
        ,t.title
    

    我在这里使用左外连接,因为我将从它开始并对数据进行一些分析。

    我经常使用这种类型的东西进行分析、报告和数据迁移。我也将它用于计费计算 - 但是我绝对没有对这种方法进行任何性能测试。重点一直是编写易于维护且具有您想要的所有功能的查询(使用高度非规范化的数据,如每日细分数据,分析往往更容易)

    【讨论】:

    • 嗨!我正试图让这个尽可能高效。我还认为(没有实际尝试)您的解决方案存在问题:如果我以后返回到以前的相同薪水和头衔组合(两者之间有不同的组合),则最小值/最大值将设置该组合的跨度从组合发生的第一个日期到相同组合发生的最后一个日期,这不是我想要的。
    • 每个人都希望获得最高效率:D.. 您可以将标题和薪水表中的任何其他列添加到选择和分组依据,以确保我猜不会发生这种情况。我前几天从 gordon-linoff 那里看到的这个可能相关的答案可能会有所帮助,但我还没有弄清楚它的确切工作原理:stackoverflow.com/a/47055261/359135
    • 也..我不知道这是否效率低下,我当然已经以这种方式分析了数百万条记录而没有问题,可能值得尝试一下您的真实数据(例如如果您一次只看一名员工,那可能还不错?我想可能涉及很长的时间框架,它可能仍然很慢)
    【解决方案2】:

    我在样本数据中添加了一些记录,以解决围绕PersonID 具有多个时间范围的可能性提出的问题,其中PersonID 具有相同的TitleSalary

    答案:

    create table dbo.Salary
        (
            PersonID varchar(3)
            , StartDate date
            , EndDate date
            , Salary int
        )
    
    create table dbo.Title
        (
            PersonID varchar(3)
            , StartDate date
            , EndDate date
            , Title varchar(10)
        )
    
    insert into dbo.Salary
    values ('Me', '2017-01-01', '2017-01-31', 2000)
        , ('Me', '2017-02-01', '2017-05-31', 2100)
        , ('Me', '2017-06-01', '2017-07-31', 2300)
        , ('You', '2017-01-01', '2017-03-31', 2400)
        , ('You', '2017-04-01', '2017-08-31', 2500)
        , ('You', '2017-09-01', '2017-12-31', 2400)     
    
    insert into dbo.Title
    values ('Me', '2017-01-01', '2017-03-31', 'Junior')
         , ('Me', '2017-04-01', '2017-07-31', 'Senior')
         , ('You', '2017-01-01', '2017-02-28', 'Junior')
         , ('You', '2017-03-01', '2017-05-31', 'Senior')
         , ('You', '2017-06-01', '2017-12-31', 'Junior')
    
    select a.PersonID
    , a.StartDate
    , a.EndDate
    , a.Salary
    , a.Title
    from (
        select s.PersonID
        , iif(s.StartDate < t.StartDate, t.StartDate, s.StartDate) as StartDate
        , iif(s.EndDate < t.EndDate, s.EndDate, t.EndDate) as EndDate
        , s.Salary
        , t.Title
        from dbo.Salary as s
        inner join dbo.Title as t on s.PersonID = t.PersonID
        ) as a
    where 1=1
    and datediff(d, a.StartDate, a.EndDate) >= 0 --is it a valid time range?
    

    子查询复制PersonID 的所有可能的StartDate / EndDate 组合,外部查询确定该时间范围是否有效。

    输出:

    PersonID    StartDate    EndDate     Salary  Title
    Me          2017-01-01   2017-01-31  2000    Junior
    Me          2017-02-01   2017-03-31  2100    Junior
    Me          2017-04-01   2017-05-31  2100    Senior
    Me          2017-06-01   2017-07-31  2300    Senior
    You         2017-01-01   2017-02-28  2400    Junior
    You         2017-03-01   2017-03-31  2400    Senior
    You         2017-04-01   2017-05-31  2500    Senior
    You         2017-06-01   2017-08-31  2500    Junior
    You         2017-09-01   2017-12-31  2400    Junior
    

    【讨论】:

      猜你喜欢
      • 2022-01-10
      • 2010-09-20
      • 1970-01-01
      • 1970-01-01
      • 2017-10-24
      • 1970-01-01
      • 2018-10-30
      • 2015-08-09
      • 1970-01-01
      相关资源
      最近更新 更多