【问题标题】:SQL to ensure unique node names in adjacency listSQL 确保邻接表中节点名的唯一性
【发布时间】:2021-12-04 03:55:18
【问题描述】:

所以我有一个邻接列表,它形成了一个模拟版本化文件结构的层次结构。问题是传入的文件名当前不是唯一的,它们必须是唯一的。为了让事情稍微更有趣,文件可能有不同的版本,应该保留第一个版本的名称(注意所有版本都有相同的 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

*我还应该注意,文件夹名称已经保证是唯一的(它们已经存在,是传入的文档)并且它们只有 1 个版本。


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,所以虽然我可以找到需要重命名的文档的名称 - 我不知道如何使用它来选择名称的第二次出现(按 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) 来自哪里?
  • :) 抱歉,我本来打算返回并将节点 (3, 11) 添加到示例中,以显示更多需要重命名的项目。

标签: sql sql-server adjacency-list


【解决方案1】:

我会假设目标结果中的最后一行 (3, 11) 是错误的。

您可以在子查询中使用窗口函数找到重复的名称,然后在更新期间将其加入。简而言之,您可以这样做:

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 的运行示例。

【讨论】:

  • 我能够将其调整为我的现实世界解决方案,它就像一个魅力。看起来关键是理解partition by 和窗口函数是如何工作的。我无法让row_number()partition by 一起工作,并且从根本上不理解错误消息说它需要order by 子句。至少我没有完全叫错树。
  • 很好,但不知道为什么需要自加入
  • @Charlieface 我也不确定。我想我是从某处的旧帖子中学到的,并且从未回头。很好的解决方案 +1。
  • 可能是因为所有其他 RDBMS 都不允许这样做并且需要自联接。我认为 SQL Server 是唯一的。很多人都忽略了这一点:可更新视图、CTE 甚至内联 TVF
【解决方案2】:

你不需要自加入表,你可以直接更新派生表,使用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;

db<>fiddle

DENSE_RANK 将根据 ordering 子句返回相同数量的并列结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-04-20
    • 1970-01-01
    • 2015-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-21
    相关资源
    最近更新 更多