【问题标题】:Compile a table with start and end dates based off of changes and effective dates根据更改和生效日期编制包含开始日期和结束日期的表格
【发布时间】:2016-10-26 19:52:00
【问题描述】:

我正在使用 MS SQL Server 2014。我有一个跟踪员工的表,每次在应用程序中进行更改时,都会设置一个有效日期并插入一个新行,如下所示。

ID  EmployeeID  Job Title       Effective Date
1   10          Sales Agent     10/1/2016
3   10          Sales Agent     10/5/2016
7   10          Sales Agent 2   10/15/2016
9   10          Sales Agent 3   10/20/2016
15  10          Sales Agent 3   10/16/2016
2   2           BSA III         10/1/2016
4   2           BSA II          10/1/2016
14  2           BSA III         10/1/2016

我需要遍历表格,如果职位名称发生变化,则插入具有新开始和结束日期的新行,如果没有新头衔,则忽略更改,除非它覆盖了生效日期上一行。员工组的最后一行应设置结束日期为 2999 年 1 月 1 日。我有 SSIS 或 SQL 可以做到这一点,但我只是不确定从哪里开始或如何解决这个问题。该过程完成后,表格结果应如下所示。

EmployeeID  Job Title       Start Date  End Date
10          Sales Agent     10/1/2016   10/14/2016
10          Sales Agent 2   10/15/2016  10/15/2016
10          Sales Agent 3   10/16/2016  1/1/2999
2           BSA III         10/1/2016   1/1/2999

【问题讨论】:

  • 最后一行的“职位”应该是“销售代理3”吧?
  • @FLICKER 是的,应该。谢谢,我已经更新了问题。
  • @Beth 否,因为第 15 行的行 ID 会用更早的生效日期覆盖第 9 行。
  • 有什么改变可以让这个人倒退吗?我的意思是他们可以成为销售代理 2 2016 年 10 月 20 日吗?
  • @HolmesIV 是的,这是可能的。他们还可以从销售代理 2 转移到劳动力代理或组织内的任何其他职位组合。

标签: sql-server ssis


【解决方案1】:

这应该可以为您提供所需的答案,并允许同一工作有多个有效时间段。

DROP TABLE #EMP
DROP TABLE #EMPFINAL
Create table #EMP (EmployeeID INT,
             JOBTitle VARCHAR(30),
             EffectiveDate Date
            )

Create table #EMPFINAL (EmployeeID INT,
             JOBTitle VARCHAR(30),
             StateDate Date,
             ENDDATE Date
            )

INSERT INTO #EMP
values (10,'Sales Agent', '2016-10-1')
,(10,'Sales Agent', '2016-10-5')
,(10,'Sales Agent 2', '2016-10-15')
,(10,'Sales Agent 3', '2016-10-20')
,(10,'Sales Agent 3', '2016-10-16')


DECLARE @EMPID INT,
        @JOBTITLE VARCHAR(30),
        @EFFECTIVE DATE,
        @MAXEFF DATE,
        @MAXEMPID INT,
        @MAXJOB VARCHAR(30)


DECLARE REV CURSOR FOR
SELECT EMPLOYEEID, JOBTITLE,EFFECTIVEDATE
FROM #EMP
order by effectivedate

OPEN REV

FETCH NEXT FROM REV
INTO @EMPID, @JOBTITLE,@EFFECTIVE

WHILE @@FETCH_STATUS = 0
BEGIN

SET @MAXEFF = (SELECT ISNULL(MAX(EndDate),'1900-01-01') FROM #EMPFINAL WHERE EmployeeID = @EMPID)
SET @MAXEMPID = (SELECT MAX(EMPLOYEEID) FROM #EMPFINAL WHERE ENDDATE = @MAXEFF)
SET @MAXJOB = (SELECT MAX(JOBTitle) FROM #EMPFINAL WHERE ENDDATE = @MAXEFF)

IF @MAXEFF = '1900-01-01'
BEGIN
    INSERT INTO #EMPFINAL
    VALUES(@EMPID,@JOBTITLE,@EFFECTIVE,'2999-01-01')

END

IF @MAXEMPID = @EMPID and @MAXJOB != @JOBTITLE
BEGIN

    UPDATE #EMPFINAL
    SET ENDDATE = DATEADD(dd,-1,@Effective)
    where ENDDATE = '2999-01-01'
    and JOBTitle != @JOBTITLE

    INSERT INTO #EMPFINAL
    VALUES(@EMPID,@JOBTITLE,@EFFECTIVE,'2999-01-01')
END


FETCH NEXT FROM REV
INTO @EMPID, @JOBTITLE,@EFFECTIVE

END

CLOSE REV;
DEALLOCATE REV;

Select * 
From #EMPFINAL

【讨论】:

    【解决方案2】:

    创建测试数据:

    select * into #t
    from (
    select 
    1 ID, 10 EmployeeID, 'Sales Agent' JobTitle, cast('10/1/2016' as date) EffectiveDate
    union select 3   ,10 ,'Sales Agent'     ,'10/5/2016'
    union select 7   ,10 ,'Sales Agent 2'   ,'10/15/2016'
    union select 9   ,10 ,'Sales Agent 3'   ,'10/20/2016'
    union select 15  ,10 ,'Sales Agent 3'   ,'10/16/2016'
    union select 2   ,2  ,'BSA III'         ,'10/1/2016'
    union select 4   ,2  ,'BSA II'          ,'10/1/2016'
    union select 14  ,2  ,'BSA III'         ,'10/1/2016'
    ) a
    

    代码:

    ;with cc as (
    select ID, EmployeeID, JobTitle, EffectiveDate
      , LEAD(JobTitle) over (partition by EmployeeID order by EffectiveDate, ID) NextJobTitle
      , LAG(EffectiveDate) over (partition by EmployeeID, JobTitle order by EffectiveDate, ID) StartDate
      , LEAD(EffectiveDate) over (partition by EmployeeID order by EffectiveDate, ID) EndDate
      from #t
    )
    , c2 as (
      select ID, EmployeeID
      , JobTitle
      , EffectiveDate
      , NextJobTitle
      , StartDate
      , dateadd(day, -1, EndDate) EndDate
    from cc
    )
    , c3 as (
    select 
      EmployeeID, JobTitle
      , IsNull(StartDate, EffectiveDate) as StartDate
      , IsNull(EndDate, '2999-01-01') as EndDate
    from c2
    where JobTitle <> NextJobTitle or NextJobTitle is null
    )
    select *
    from c3
    where StartDate <= EndDate
    

    结果:

    ╔════════════╦═══════════════╦════════════╦════════════╗
    ║ EmployeeID ║ JobTitle      ║ StartDate  ║ EndDate    ║
    ╠════════════╬═══════════════╬════════════╬════════════╣
    ║ 2          ║ BSA III       ║ 2016-10-01 ║ 2999-01-01 ║
    ╠════════════╬═══════════════╬════════════╬════════════╣
    ║ 10         ║ Sales Agent   ║ 2016-10-01 ║ 2016-10-14 ║
    ╠════════════╬═══════════════╬════════════╬════════════╣
    ║ 10         ║ Sales Agent 2 ║ 2016-10-15 ║ 2016-10-15 ║
    ╠════════════╬═══════════════╬════════════╬════════════╣
    ║ 10         ║ Sales Agent 3 ║ 2016-10-16 ║ 2999-01-01 ║
    ╚════════════╩═══════════════╩════════════╩════════════╝
    

    【讨论】:

    • 您好闪烁,感谢您的回答,但我的示例数据是正确的。稍后添加的插入(更高的 ID 号)应该能够覆盖更改,如果它们的生效日期更早。
    • 很好地利用了领先和滞后,我只有 2008 年所以我做了一个游标:(
    • ID 越大,EffectiveDate 越小?这有点不寻常。我看到你已经改变了你的样本数据。我试着调整我的答案。
    • @ScholarYoshi,我用你最近的样本数据调整了我的答案
    • @HolmesIV,谢谢。在以前的 SQL 中实现 LEAD 和 LAG 很麻烦但可行。如果数据集很小,光标还不错。 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-19
    • 1970-01-01
    • 2021-07-21
    • 1970-01-01
    相关资源
    最近更新 更多