最大菊花链长度

问题描述

我在表中有用户的一堆值对(之前,之后)。在理想情况下,这些值应形成一个完整的链。例如

| 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;