根据记录修改日期和活动标志创建开始和结束日期
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
我正在尝试根据具有主键 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