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

时间:2019-08-06 18:38:36

标签: sql-server gaps-and-islands

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

我试图找出每条Contact记录的开始(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小提琴:

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

2 个答案:

答案 0 :(得分:1)

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

如果当前行的StateCode为0,而前一行为NULL(第一行)或1,则此行的日期为“ StartDate”。如果相反,则为“ EndDate”。如果两行都具有相同的StateCode,则只需忽略该行。

答案 1 :(得分:0)

我可以通过使用LAG函数为StartDates设置标志,并由StateCode本身为EndDates设置标志来使其工作。然后,我在DENSE_RANK函数上对Start和End标志做了一些条件逻辑,以获取与日期周期相关的实际日期。这是我的代码(仍需要格式化)。

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