使用 RANK OVER PARTITION 比较前一行结果
Using RANK OVER PARTITION to Compare a Previous Row Result
我正在处理一个包含(在其他列中)用户 ID 和开始日期的数据集。目标是有一个新列“isRehire”,将他们的开始日期与以前的开始日期进行比较。
如果 startDates 之间的差异在 1 年以内,则为 Rehire = Y。
当用户的开始日期超过 2 个时,困难和我的问题就会出现。如果第三个和第一个开始日期之间的差异超过一年,则第三个开始日期将是重新雇用的新“基准日期”。
userID
startDate
isRehire
123
07/24/19
N
123
02/04/20
Y
123
08/25/20
N
123
12/20/20
Y
123
06/15/21
Y
123
08/20/21
Y
123
08/30/21
N
在上面的示例中,您可以直观地看到问题。第一个 startDate 07/24/19,用户不是 Rehire。第二个 startDate 02/04/20,他们是 Rehire。 3rd 开始日期为 2020 年 8 月 25 日的用户 不是 重新雇用,因为距离他们最初的开始日期已经超过 1 年了。这是新的“锚定”日期。
接下来的 3 个实例都是 Y,因为它们都在新“锚定”日期 08/25/20 的 1 年内。 08/30/21 的最终开始日期超过 08/25/20 一年多,表示“N”,“周期”再次重置,08/30/21 作为新的“锚定”日期。
我的目标是利用 RANK OVER PARTITION 来完成这个,因为根据我的测试,我相信必须有一种方法可以为日期分配等级,然后可以将其包装在 select 语句中用于写入 CASE 表达式。虽然完全有可能我完全找错了树。
您可以在下面看到我试图用来完成此操作的一些代码,尽管到目前为止还没有取得太大成功。
select TestRank,
startDate,
userID,
CASE WHEN TestRank = TestRank THEN (TestRank - 1
) ELSE '' END AS TestRank2
from
(
select userID,
startDate
RANK() OVER (PARTITION BY userID
ORDER BY startDate desc)
as TestRank
from [MyTable] a
WHERE a.userID = [int]
) b
逻辑复杂,window功能不够。要解决这个问题,您需要迭代——或者用 SQL 来说,递归 CTE:
with t as (
select t.*, row_number() over (partition by id order by startdate) as seqnum
from mytable t
),
cte as (
select t.id, t.startdate, t.seqnum, 'N' as isrehire, t.startdate as anchordate
from t
where seqnum = 1
union all
select t.id, t.startdate, t.seqnum,
(case when t.startdate > dateadd(year, 1, cte.anchordate) then 'N' else 'Y' end),
(case when t.startdate > dateadd(year, 1, cte.anchordate) then t.startdate else cte.anchordate end)
from cte join
t
on t.seqnum = cte.seqnum + 1
)
select *
from cte
order by id, startdate;
Here 是一个 db<>fiddle.
我正在处理一个包含(在其他列中)用户 ID 和开始日期的数据集。目标是有一个新列“isRehire”,将他们的开始日期与以前的开始日期进行比较。
如果 startDates 之间的差异在 1 年以内,则为 Rehire = Y。
当用户的开始日期超过 2 个时,困难和我的问题就会出现。如果第三个和第一个开始日期之间的差异超过一年,则第三个开始日期将是重新雇用的新“基准日期”。
userID | startDate | isRehire |
---|---|---|
123 | 07/24/19 | N |
123 | 02/04/20 | Y |
123 | 08/25/20 | N |
123 | 12/20/20 | Y |
123 | 06/15/21 | Y |
123 | 08/20/21 | Y |
123 | 08/30/21 | N |
在上面的示例中,您可以直观地看到问题。第一个 startDate 07/24/19,用户不是 Rehire。第二个 startDate 02/04/20,他们是 Rehire。 3rd 开始日期为 2020 年 8 月 25 日的用户 不是 重新雇用,因为距离他们最初的开始日期已经超过 1 年了。这是新的“锚定”日期。
接下来的 3 个实例都是 Y,因为它们都在新“锚定”日期 08/25/20 的 1 年内。 08/30/21 的最终开始日期超过 08/25/20 一年多,表示“N”,“周期”再次重置,08/30/21 作为新的“锚定”日期。
我的目标是利用 RANK OVER PARTITION 来完成这个,因为根据我的测试,我相信必须有一种方法可以为日期分配等级,然后可以将其包装在 select 语句中用于写入 CASE 表达式。虽然完全有可能我完全找错了树。
您可以在下面看到我试图用来完成此操作的一些代码,尽管到目前为止还没有取得太大成功。
select TestRank,
startDate,
userID,
CASE WHEN TestRank = TestRank THEN (TestRank - 1
) ELSE '' END AS TestRank2
from
(
select userID,
startDate
RANK() OVER (PARTITION BY userID
ORDER BY startDate desc)
as TestRank
from [MyTable] a
WHERE a.userID = [int]
) b
逻辑复杂,window功能不够。要解决这个问题,您需要迭代——或者用 SQL 来说,递归 CTE:
with t as (
select t.*, row_number() over (partition by id order by startdate) as seqnum
from mytable t
),
cte as (
select t.id, t.startdate, t.seqnum, 'N' as isrehire, t.startdate as anchordate
from t
where seqnum = 1
union all
select t.id, t.startdate, t.seqnum,
(case when t.startdate > dateadd(year, 1, cte.anchordate) then 'N' else 'Y' end),
(case when t.startdate > dateadd(year, 1, cte.anchordate) then t.startdate else cte.anchordate end)
from cte join
t
on t.seqnum = cte.seqnum + 1
)
select *
from cte
order by id, startdate;
Here 是一个 db<>fiddle.