SQL 使用 ID 字段跟踪记录历史
SQL Trace record history using ID fields
首先,如果这个问题的主题不清楚或使用了错误的术语,我深表歉意,但我不太确定我正在尝试做的事情叫什么。我正在使用 SQL Server 2016。我有一个 table,其中包含以下三列:
create table #temp (PrevID varchar(10),ID varchar(10), NextID varchar(10))
insert into #temp
Select NULL,'ABC1','ABC3' Union all
Select NULL,'ABC2','ABC4' Union all
Select 'ABC1','ABC3','ABC9' Union all
Select 'ABC2','ABC4','ABC10' Union all
Select 'ABC3','ABC9',NULL Union all
Select 'ABC4','ABC10','ABC25' Union all
Select 'ABC10','ABC25',NULL
PrevID |ID | NextID
NULL |ABC1 | ABC3
NULL |ABC2 | ABC4
ABC1 |ABC3 | ABC9
ABC2 |ABC4 | ABC10
ABC3 |ABC9 | NULL
ABC4 |ABC10| ABC25
ABC10 |ABC25| NULL
我想要做的是获取彼此相关的 ID 的输出。我不能简单地按 ID 升序排序,因为 ABC1 和 ABC2 是两个不同链的一部分,虽然每个链都是顺序的,但它们不是连续的。
通常我会使用 CTE 或子查询将 table 加入自身,以便能够查询下一条或上一条记录,但是对于有多少条记录将成为一个记录的一部分没有具体限制链,所以我无法提前知道我需要重新加入 table 的次数。不幸的是,我也无法更改 table 的结构来添加 ChainID。
所需的输出如下所示:
ChainID|PrevID |ID | NextID
Chain1 |NULL |ABC1 | ABC3
Chain1 |ABC1 |ABC3 | ABC9
Chain1 |ABC3 |ABC9 | NULL
Chain2 |NULL |ABC2 | ABC4
Chain2 |ABC2 |ABC4 | ABC10
Chain2 |ABC4 |ABC10| ABC25
Chain2 |ABC10 |ABC25| NULL
谢谢,感谢任何帮助
-- 编辑以包括我用来创建 table 的代码,例如
这是一个图行走问题。以下应获取每个连接组的最小 ID。
with edges as (
select distinct v.id1, v.id2
from t cross apply
(values (prev_id, id), (id, prev_id), (next_id, id), (id, next_id)
) v(id1, id2)
where id1 is not null and id2 is not null
),
cte as (
select id1, id1 as id2,
convert(varchar(max), concat(',', id1, ',')) as ids
from edges
union all
select cte.id1, e.id2,
concat(ids, e.id2 + ',')
from cte join
edges e
on cte.id2 = e.id1
where cte.ids not like concat('%,', e.id2, ',%')
)
select id1, min(id2) as grp, dense_rank() over (order by min(id2)) as grp_num
from cte
group by id1;
然后您可以 join
返回原始数据以将组分配给原始行。
Here 是一个 db<>fiddle.
感谢@Gordon Linoff 用图表行走为我指明了正确的方向,这让我在递归查询 post SQL SERVER – Introduction to Hierarchical Query using a Recursive CTE – A Primer
上找到了这个 post
我意识到我真的不需要双向查看每条记录来获取记录的历史记录,因为每条记录都会有一个 "NextID" 直到最后一条为 NULL 我可以从基本记录开始跟随它。我提出了以下查询,它在不到一秒的时间内完成了我的 ~700 行样本测试。
create table #temp (PrevID varchar(10),ID varchar(10), NextID varchar(10))
insert into #temp
Select NULL,'ABC1','ABC3' Union all
Select NULL,'ABC2','ABC4' Union all
Select 'ABC1','ABC3','ABC9' Union all
Select 'ABC2','ABC4','ABC10' Union all
Select 'ABC3','ABC9',NULL Union all
Select 'ABC4','ABC10','ABC25' Union all
Select 'ABC10','ABC25',NULL
;
with descendants as
( select id as ParentID, nextid as ChildID, 2 as rn
from #temp
union all
select d.ParentID, s.nextid, d.rn + 1
from descendants as d
join #temp s on d.ChildID = s.id
)
select *
from descendants a
where not exists
(select distinct d.ChildID
from descendants d
where d.ChildID = a.ParentID)
and ChildID is not null
union all
select id,id, 1
from #temp t
where t.previd is null
order by 1,3
首先,如果这个问题的主题不清楚或使用了错误的术语,我深表歉意,但我不太确定我正在尝试做的事情叫什么。我正在使用 SQL Server 2016。我有一个 table,其中包含以下三列:
create table #temp (PrevID varchar(10),ID varchar(10), NextID varchar(10))
insert into #temp
Select NULL,'ABC1','ABC3' Union all
Select NULL,'ABC2','ABC4' Union all
Select 'ABC1','ABC3','ABC9' Union all
Select 'ABC2','ABC4','ABC10' Union all
Select 'ABC3','ABC9',NULL Union all
Select 'ABC4','ABC10','ABC25' Union all
Select 'ABC10','ABC25',NULL
PrevID |ID | NextID
NULL |ABC1 | ABC3
NULL |ABC2 | ABC4
ABC1 |ABC3 | ABC9
ABC2 |ABC4 | ABC10
ABC3 |ABC9 | NULL
ABC4 |ABC10| ABC25
ABC10 |ABC25| NULL
我想要做的是获取彼此相关的 ID 的输出。我不能简单地按 ID 升序排序,因为 ABC1 和 ABC2 是两个不同链的一部分,虽然每个链都是顺序的,但它们不是连续的。
通常我会使用 CTE 或子查询将 table 加入自身,以便能够查询下一条或上一条记录,但是对于有多少条记录将成为一个记录的一部分没有具体限制链,所以我无法提前知道我需要重新加入 table 的次数。不幸的是,我也无法更改 table 的结构来添加 ChainID。
所需的输出如下所示:
ChainID|PrevID |ID | NextID
Chain1 |NULL |ABC1 | ABC3
Chain1 |ABC1 |ABC3 | ABC9
Chain1 |ABC3 |ABC9 | NULL
Chain2 |NULL |ABC2 | ABC4
Chain2 |ABC2 |ABC4 | ABC10
Chain2 |ABC4 |ABC10| ABC25
Chain2 |ABC10 |ABC25| NULL
谢谢,感谢任何帮助
-- 编辑以包括我用来创建 table 的代码,例如
这是一个图行走问题。以下应获取每个连接组的最小 ID。
with edges as (
select distinct v.id1, v.id2
from t cross apply
(values (prev_id, id), (id, prev_id), (next_id, id), (id, next_id)
) v(id1, id2)
where id1 is not null and id2 is not null
),
cte as (
select id1, id1 as id2,
convert(varchar(max), concat(',', id1, ',')) as ids
from edges
union all
select cte.id1, e.id2,
concat(ids, e.id2 + ',')
from cte join
edges e
on cte.id2 = e.id1
where cte.ids not like concat('%,', e.id2, ',%')
)
select id1, min(id2) as grp, dense_rank() over (order by min(id2)) as grp_num
from cte
group by id1;
然后您可以 join
返回原始数据以将组分配给原始行。
Here 是一个 db<>fiddle.
感谢@Gordon Linoff 用图表行走为我指明了正确的方向,这让我在递归查询 post SQL SERVER – Introduction to Hierarchical Query using a Recursive CTE – A Primer
上找到了这个 post我意识到我真的不需要双向查看每条记录来获取记录的历史记录,因为每条记录都会有一个 "NextID" 直到最后一条为 NULL 我可以从基本记录开始跟随它。我提出了以下查询,它在不到一秒的时间内完成了我的 ~700 行样本测试。
create table #temp (PrevID varchar(10),ID varchar(10), NextID varchar(10))
insert into #temp
Select NULL,'ABC1','ABC3' Union all
Select NULL,'ABC2','ABC4' Union all
Select 'ABC1','ABC3','ABC9' Union all
Select 'ABC2','ABC4','ABC10' Union all
Select 'ABC3','ABC9',NULL Union all
Select 'ABC4','ABC10','ABC25' Union all
Select 'ABC10','ABC25',NULL
;
with descendants as
( select id as ParentID, nextid as ChildID, 2 as rn
from #temp
union all
select d.ParentID, s.nextid, d.rn + 1
from descendants as d
join #temp s on d.ChildID = s.id
)
select *
from descendants a
where not exists
(select distinct d.ChildID
from descendants d
where d.ChildID = a.ParentID)
and ChildID is not null
union all
select id,id, 1
from #temp t
where t.previd is null
order by 1,3