我正在尝试基于具有主键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
答案 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