问题描述
我在表中有用户的一堆值对(之前,之后)。在理想情况下,这些值应形成一个完整的链。例如
| UserId | Before | After |
|--------|--------|-------|
| 1 | 0 | 10 |
| 1 | 10 | 20 |
| 1 | 20 | 30 |
| 1 | 30 | 40 |
| 1 | 40 | 30 |
| 1 | 30 | 52 |
| 1 | 52 | 0 |
不幸的是,这些记录源自多个不同的表,并被导入到我的调查表中。该表中的其他值不适合排序(例如CreatedDate),这是因为系统中存在一些古怪之处,使它们混乱无序。
| UserId | Before | After |
|--------|--------|-------|
| 1 | 0 | 10 |
| 1 | 10 | 20 |
| 1 | 20 | 30 |
// Row Deleted (30->40)
| 1 | 40 | 30 |
| 1 | 30 | 52 |
| 1 | 52 | 0 |
我已经看过关于SO的其他菊花链问题(通常是在线的),但是它们似乎都在给定的问题空间中,该对中的一个值始终以可预测的方式低于另一个。就我而言,可以增加或减少。
有没有一种方法可以快速计算可以创建的最长链?我确实有一个CreatedAt
列,该列会提供一些(非常粗糙的)相对排序-当日期相隔约10秒以上时,我们可以认为它们是可排序的)
解决方法
因此,您难道不就此简单地获得“链”断裂的第一行吗?
SELECT UserID,Before,After
FROM dbo.YourTable YT
WHERE NOT EXISTS (SELECT 1
FROM dbo.YourTable NE
WHERE NE.After = YT.Before)
AND YT.Before != 0;
如果要在最后一行的“链”所在的行断开,只需在WHERE
的{{1}}的列上交换别名。
以下内容对示例数据进行分层递归,并计算一个称为“ h_level”的“链”计数列。
;with recur_cte([UserId],[Before],[After],h_level) as (
select [UserId],0
from dbo.test_table
where [Before] is null
union all
select tt.[UserId],tt.[Before],tt.[After],rc.h_level+1
from dbo.test_table tt join recur_cte rc on tt.UserId=rc.UserId
and tt.[Before]=rc.[After]
where tt.[Before]<tt.[after])
select * from recur_cte;
结果:
UserId Before After h_level
1 NULL 10 0
1 10 20 1
1 20 30 2
1 30 40 3
1 30 52 3
这有帮助吗?您可以进一步定义要排除的行吗?
,如果您希望拥有多个链的用户:
select t.UserID
from <T> as t left outer join <T> as t2
on t2.UserID = t.UserID and t2.Before = t.After
where t2.UserID is null
group by t.UserID
having count(*) > 1;