SQL 确保邻接表中的节点名称唯一
SQL to ensure unique node names in adjacency list
所以我有一个邻接列表,它形成一个模拟版本化文件结构的层次结构。问题是传入的文件名当前不是唯一的,它们需要是唯一的。为了让事情 稍微 更有趣,文件可能有不同的版本,应该保留第一个版本的名称(注意所有版本都有相同的 NodeID)。
邻接表
ParentID
NodeID
VersionNum
FileName
-1
1
1
FirstFolder
1
2
1
SecondFolder
1
3
1
ThirdFolder
1
4
1
FirstDocument
1
4
2
FirstDocument
1
5
1
FirstDocument
1
5
2
FirstDocument
2
6
1
FirstDocument
2
6
2
FirstDocument
2
7
1
SecondDocument
3
8
1
SecondDocument
3
9
1
ThirdDocument
3
9
2
ThirdDocument
3
10
1
ThirdDocument
3
11
1
ThirdDocument
目标结果
ParentID
NodeID
VersionNum
FileName
-1
1
1
FirstFolder
1
2
1
SecondFolder
1
3
1
ThirdFolder
1
4
1
FirstDocument
1
4
2
FirstDocument
1
5
1
FirstDocument_1
1
5
2
FirstDocument_1
2
6
1
FirstDocument
2
6
2
FirstDocument
2
7
1
SecondDocument
3
8
1
SecondDocument
3
9
1
ThirdDocument
3
9
2
ThirdDocument
3
10
1
ThirdDocument_1
3
11
1
ThirdDocument_2
*我还要注意,文件夹名称已经保证是唯一的(它们已经存在,是传入的文档)并且它们只有一个版本。
CREATE TABLE #tmp_tree
(
ParentID INT,
NodeID INT,
VersionNum INT,
FileName VARCHAR(50),
);
INSERT INTO #tmp_tree (ParentID, NodeID, VersionNum, FileName)
VALUES (-1, 1, 1, 'FirstFolder' ),
(1, 2, 1, 'SecondFolder' ),
(1, 3, 1, 'ThirdFolder' ),
(1, 4, 1, 'FirstDocument' ),
(1, 4, 2, 'FirstDocument' ),
(1, 5, 1, 'FirstDocument' ),
(1, 5, 2, 'FirstDocument' ),
(2, 6, 1, 'FirstDocument' ),
(2, 6, 2, 'FirstDocument' ),
(2, 7, 1, 'SecondDocument' ),
(3, 8, 1, 'SecondDocument' ),
(3, 9, 1, 'ThirdDocument' ),
(3, 9, 2, 'ThirdDocument' ),
(3, 10, 1, 'ThirdDocument' )
(3, 11, 1, 'ThirdDocument' )
我真的不知道如何通过存储过程来解决这个问题。邻接列表向我尖叫 CTE,但这让我无法真正快速地发挥作用。 Group By 丢失了 NodeID,因此虽然我可以找到需要重命名的文档的名称 - 我不知道如何将其用于 select 第二次出现的名称(按 NodeID 排序)。
-- I don't see how this helps... but this finds the names that need to change.
select ParentID, FileName,VersionNum, count(*) from #tmp_tree
GROUP BY ParentID, FileName, VersionNum
HAVING VersionNum = 1 and count(*) > 1
order by FileName
我知道如何解决这个程序但不是声明式的。
不知道这样离解更近了还是更远了:
select f2.*, Row_Number() over (order by f2.FileName) from
(select top 10 f.*, count(FileName) over (PARTITION by ParentID, FileName) as n from (select * from #tmp_tree where versionNum = 1) as f
order by f.ParentID, f.FileName) as f2
Where n > 1
我假设目标结果中的最后一行 (3, 11) 是错误的。
您可以在子查询中使用 window 函数找到重复的名称,然后在更新期间加入它。简而言之,你可以这样做:
update #tmp_tree
set #tmp_tree.filename = concat(#tmp_tree.filename, '_', x.rn)
from #tmp_tree
join (
select *,
row_number() over(partition by parentid, filename order by nodeid) as rn
from #tmp_tree
where versionnum = 1
) x on x.rn > 1 and x.nodeid = #tmp_tree.nodeid;
结果:
ParentID NodeID VersionNum FileName
--------- ------- ----------- ---------------
-1 1 1 FirstFolder
1 2 1 SecondFolder
1 3 1 ThirdFolder
1 4 1 FirstDocument
1 4 2 FirstDocument
1 5 1 FirstDocument_2
1 5 2 FirstDocument_2
2 6 1 FirstDocument
2 6 2 FirstDocument
2 7 1 SecondDocument
3 8 1 SecondDocument
3 9 1 ThirdDocument
3 9 2 ThirdDocument
3 10 1 ThirdDocument_2
请参阅 db<>fiddle 中的 运行 示例。
你不需要自己加入table,你可以直接更新派生的table,计算行号后使用DENSE_RANK
update x
set filename = concat(x.filename, '_', x.rn)
from (
select *,
dense_rank() over(partition by parentid, filename order by nodeid) as rn
from #tmp_tree
) x
where x.rn > 1;
根据排序条款,DENSE_RANK
将 return 相同数量的并列结果。
所以我有一个邻接列表,它形成一个模拟版本化文件结构的层次结构。问题是传入的文件名当前不是唯一的,它们需要是唯一的。为了让事情 稍微 更有趣,文件可能有不同的版本,应该保留第一个版本的名称(注意所有版本都有相同的 NodeID)。
邻接表
ParentID | NodeID | VersionNum | FileName |
---|---|---|---|
-1 | 1 | 1 | FirstFolder |
1 | 2 | 1 | SecondFolder |
1 | 3 | 1 | ThirdFolder |
1 | 4 | 1 | FirstDocument |
1 | 4 | 2 | FirstDocument |
1 | 5 | 1 | FirstDocument |
1 | 5 | 2 | FirstDocument |
2 | 6 | 1 | FirstDocument |
2 | 6 | 2 | FirstDocument |
2 | 7 | 1 | SecondDocument |
3 | 8 | 1 | SecondDocument |
3 | 9 | 1 | ThirdDocument |
3 | 9 | 2 | ThirdDocument |
3 | 10 | 1 | ThirdDocument |
3 | 11 | 1 | ThirdDocument |
目标结果
ParentID | NodeID | VersionNum | FileName |
---|---|---|---|
-1 | 1 | 1 | FirstFolder |
1 | 2 | 1 | SecondFolder |
1 | 3 | 1 | ThirdFolder |
1 | 4 | 1 | FirstDocument |
1 | 4 | 2 | FirstDocument |
1 | 5 | 1 | FirstDocument_1 |
1 | 5 | 2 | FirstDocument_1 |
2 | 6 | 1 | FirstDocument |
2 | 6 | 2 | FirstDocument |
2 | 7 | 1 | SecondDocument |
3 | 8 | 1 | SecondDocument |
3 | 9 | 1 | ThirdDocument |
3 | 9 | 2 | ThirdDocument |
3 | 10 | 1 | ThirdDocument_1 |
3 | 11 | 1 | ThirdDocument_2 |
*我还要注意,文件夹名称已经保证是唯一的(它们已经存在,是传入的文档)并且它们只有一个版本。
CREATE TABLE #tmp_tree
(
ParentID INT,
NodeID INT,
VersionNum INT,
FileName VARCHAR(50),
);
INSERT INTO #tmp_tree (ParentID, NodeID, VersionNum, FileName)
VALUES (-1, 1, 1, 'FirstFolder' ),
(1, 2, 1, 'SecondFolder' ),
(1, 3, 1, 'ThirdFolder' ),
(1, 4, 1, 'FirstDocument' ),
(1, 4, 2, 'FirstDocument' ),
(1, 5, 1, 'FirstDocument' ),
(1, 5, 2, 'FirstDocument' ),
(2, 6, 1, 'FirstDocument' ),
(2, 6, 2, 'FirstDocument' ),
(2, 7, 1, 'SecondDocument' ),
(3, 8, 1, 'SecondDocument' ),
(3, 9, 1, 'ThirdDocument' ),
(3, 9, 2, 'ThirdDocument' ),
(3, 10, 1, 'ThirdDocument' )
(3, 11, 1, 'ThirdDocument' )
我真的不知道如何通过存储过程来解决这个问题。邻接列表向我尖叫 CTE,但这让我无法真正快速地发挥作用。 Group By 丢失了 NodeID,因此虽然我可以找到需要重命名的文档的名称 - 我不知道如何将其用于 select 第二次出现的名称(按 NodeID 排序)。
-- I don't see how this helps... but this finds the names that need to change.
select ParentID, FileName,VersionNum, count(*) from #tmp_tree
GROUP BY ParentID, FileName, VersionNum
HAVING VersionNum = 1 and count(*) > 1
order by FileName
我知道如何解决这个程序但不是声明式的。
不知道这样离解更近了还是更远了:
select f2.*, Row_Number() over (order by f2.FileName) from
(select top 10 f.*, count(FileName) over (PARTITION by ParentID, FileName) as n from (select * from #tmp_tree where versionNum = 1) as f
order by f.ParentID, f.FileName) as f2
Where n > 1
我假设目标结果中的最后一行 (3, 11) 是错误的。
您可以在子查询中使用 window 函数找到重复的名称,然后在更新期间加入它。简而言之,你可以这样做:
update #tmp_tree
set #tmp_tree.filename = concat(#tmp_tree.filename, '_', x.rn)
from #tmp_tree
join (
select *,
row_number() over(partition by parentid, filename order by nodeid) as rn
from #tmp_tree
where versionnum = 1
) x on x.rn > 1 and x.nodeid = #tmp_tree.nodeid;
结果:
ParentID NodeID VersionNum FileName
--------- ------- ----------- ---------------
-1 1 1 FirstFolder
1 2 1 SecondFolder
1 3 1 ThirdFolder
1 4 1 FirstDocument
1 4 2 FirstDocument
1 5 1 FirstDocument_2
1 5 2 FirstDocument_2
2 6 1 FirstDocument
2 6 2 FirstDocument
2 7 1 SecondDocument
3 8 1 SecondDocument
3 9 1 ThirdDocument
3 9 2 ThirdDocument
3 10 1 ThirdDocument_2
请参阅 db<>fiddle 中的 运行 示例。
你不需要自己加入table,你可以直接更新派生的table,计算行号后使用DENSE_RANK
update x
set filename = concat(x.filename, '_', x.rn)
from (
select *,
dense_rank() over(partition by parentid, filename order by nodeid) as rn
from #tmp_tree
) x
where x.rn > 1;
根据排序条款,DENSE_RANK
将 return 相同数量的并列结果。