根据记录修改日期和活动标志创建开始和结束日期

Create Start and End Dates based on record Modification Date and Active Flag

我正在尝试根据具有主键 ContactID、ModificationDate 和 StateCode 的 table 创建开始日期和结束日期(生效日期)。 ModificationDate 表示记录何时被输入或更新,StateCode 表示记录是启用还是禁用。

我正在尝试找出每个联系人记录的开始 (StateCode = 0) 和结束 (StateCode=1) 日期,但无法完全理解查询。

我试过使用 Windows 函数的组合,例如 Row_Number、Rank、Lead 等,但不知道如何在 StateCode = 1 时增加分组数。

CREATE TABLE Contact(
  ContactID INTEGER,
  StateCode INTEGER,
  ModifiedOn Datetime)

  INSERT INTO Contact
  SELECT 1, 0, '7/1/2019' UNION
  SELECT 1, 0, '7/2/2019' UNION
  SELECT 1, 1, '7/3/2019' UNION
  SELECT 1, 0, '7/4/2019' UNION
  SELECT 1, 0, '7/5/2019' UNION
  SELECT 1, 1, '7/6/2019' UNION
  SELECT 1, 0, '7/7/2019' UNION
  SELECT 1, 0, '7/8/2019' 

示例 SQL Fiddle:

http://sqlfiddle.com/#!18/e8aca/45

根据我例子中StateCode的变化,我预计会看到3条记录。

ContactID, StartDate, EndDate, ActiveFlag
1, 7/1/2019, 7/3/2019, 0
1, 7/4/2019, 7/6/2019, 0
1, 7/7/2019, NULL, 1

我正在验证的潜在解决方案

WITH CTE AS (
SELECT
  LAG(StateCode,1,1) OVER (PARTITION BY ContactID ORDER BY ModifiedOn) AS IsStart
  , StateCode AS IsEnd
  , ContactID
  , StateCode
  , ModifiedOn  
FROM Contact       
), CTE2 AS(
SELECT 
  ContactID
  , IsStart
  , IsEnd  
  , ModifiedOn  
  , DENSE_RANK() OVER (PARTITION BY ContactID ORDER BY CASE WHEN IsStart = 1 THEN ModifiedOn END) AS StartTest2
  , DENSE_RANK() OVER (PARTITION BY ContactID ORDER BY CASE WHEN IsEnd = 1 THEN ModifiedOn END) AS EndTest2
FROM CTE
WHERE IsStart = 1 OR IsEnd = 1
)
SELECT 
  Start.ContactID  
  , Start.ModifiedOn AS StartDate
  , EndDates.ModifiedOn AS EndDate
FROM CTE2 AS Start
   LEFT JOIN CTE2 AS EndDates
     ON Start.ContactID = EndDates.ContactID
     AND Start.StartTest2 = EndDates.EndTest2
WHERE Start.StartTest2 <> 1
ORDER BY Start.ModifiedOn

您可以使用 LAG() 或自连接来获取对前一行的引用。

如果当前行的 StateCode 为 0,而前一行的为 NULL(第一行)或 1,则此行的日期为 "StartDate"。如果反过来是真的,那么它就是一个 "EndDate"。如果两行具有相同的 StateCode,则忽略该行。

我能够通过使用 LAG 函数为 StartDates 设置标志并通过 StateCode 本身为 EndDates 设置标志来实现它。然后,我对开始和结束标志的 DENSE_RANK 函数执行了一些条件逻辑,以获取与日期周期关联的实际日期。这是我的代码(仍然需要格式化)。

WITH CTE AS (
SELECT
    CASE WHEN StateCode = 1 THEN 0 ELSE (LAG(StateCode,1,1) OVER (PARTITION BY ContactID ORDER BY ModifiedOn ASC)) END AS IsStartDate
  , CASE WHEN (LAG(StateCode,1,NULL) OVER (PARTITION BY ContactID ORDER BY ModifiedOn ASC)) IS NULL OR (LAG(StateCode,1,NULL) OVER (PARTITION BY ContactID ORDER BY ModifiedOn ASC)) = 1 THEN 0 ELSE StateCode END AS IsEndDate
  , ContactID
  , StateCode
  , ModifiedOn  
FROM Contact       
), CTE2 AS(
SELECT 
  ContactID
  , IsStartDate
  , IsEndDate  
  , ModifiedOn  
  , CASE WHEN IsStartDate = 0 THEN -1 ELSE DENSE_RANK() OVER (PARTITION BY ContactID ORDER BY CASE WHEN IsStartDate = 1 THEN ModifiedOn END ASC) END AS StartRank
  , DENSE_RANK() OVER (PARTITION BY ContactID ORDER BY CASE WHEN IsEndDate = 1 THEN ModifiedOn END ASC) AS EndRank
FROM CTE
WHERE IsStart = 1 OR IsEnd = 1
)
SELECT 
  Start.ContactID  
  , Start.ModifiedOn AS StartDate
  , EndDates.ModifiedOn AS EndDate
FROM CTE2 AS Start
   LEFT JOIN CTE2 AS EndDates
     ON Start.ContactID = EndDates.ContactID
     AND Start.StartRank = EndDates.EndRank
WHERE Start.StartRank <> -1
ORDER BY Start.ModifiedOn