【问题标题】:Extract the maximum value from the last number in a hierarchyID?从hierarchyID中的最后一个数字中提取最大值?
【发布时间】:2026-01-07 22:25:03
【问题描述】:

在 SQL Server 中,我有一列将层次结构 ID 转换为字符串。我需要为新行添加新的 hierarcyID,但首先我必须找到当前 ID 的最后一个孩子。 hierarchyID 如下所示:

/1/1/1/6/1/ 
/1/1/1/6/7/
/1/1/1/6/3/
/1/1/1/6/13/
/1/1/1/6/4/

如你所见,最大数量不等于行数,所以很遗憾我不能使用 count()+1。

我需要从这个列表中提取的是:

13

我只有 PL SQL 方面的经验,使用 regexp 函数很容易做到这一点,但我在 SQL Server 中找不到解决方案。

【问题讨论】:

  • 这不是一个解决方案,因为我在该列中也有不同的层次结构 ID。我需要提取最后一个数字的最大值。

标签: sql sql-server substring


【解决方案1】:

您可以使用下面的一些 STRING 操作来获得您想要的输出-

DEMO HERE

WITH your_table(your_column)
AS
(
    SELECT '/1/1/1/6/1/' UNION ALL
    SELECT '/1/1/1/6/7/' UNION ALL
    SELECT '/1/1/1/6/3/' UNION ALL
    SELECT '/1/1/1/6/13/' UNION ALL
    SELECT '/1/1/1/6/4/'
)

SELECT
MAX(
    CAST(
        REVERSE(
            LEFT(
                REVERSE(LEFT(your_column,LEN(your_column)-1)),
                CHARINDEX('/',REVERSE(LEFT(your_column,LEN(your_column)-1)) ,0) - 1
            )
        )
    AS INT
    )
)
FROM your_table

注意:数据必须作为您的样本数据

【讨论】:

    【解决方案2】:

    给出一些想法,2个解决方案。

    第一个适用于 Sql Server 2017 及更高版本。
    对于低于此的大多数版本,第二个。

    create table YourTable
    (
      ID int identity(101,1) primary key,
      hierarcyIDs varchar(100)
    )
    GO
    
    insert into YourTable 
     (hierarcyIDs) values
     ('/1/2/3/4/5/')
    ,('/1/2/3/4/15/')
    ,('/1/2/3/4/10/')
    ,('/11/12/13/14/15/')
    ,('/11/12/13/14/42/')
    ;
    GO
    
    5 行受影响
    SELECT
    [1] as id1,
    [2] as id2,
    [3] as id3,
    [4] as id4,
    max([5]) as id5,
    concat_ws('/','',[1],[2],[3],[4],max([5])+1,'') as nextHierarcyIDs
    FROM YourTable
    
    OUTER APPLY
    (
      select *
      from
      ( select try_cast(value as int) as id
        , row_number() over (order by (select 0)) rn
        from string_split(trim('/' from hierarcyIDs),'/') s
      ) src
      pivot (max(id) 
        for rn in ([1],[2],[3],[4],[5])
      ) pvt
    ) anarchy
    group by [1],[2],[3],[4]
    
    GO
    
    id1 | id2 | id3 | id4 | id5 | nextHierarcyIDs --: | --: | --: | --: | --: | :--------------- 1 | 2 | 3 | 4 | 15 | /1/2/3/4/16/ 11 | 12 | 13 | 14 | 42 | /11/12/13/14/43/
    SELECT hierarcyIdLvl1to4
    , MAX(hierarcyIDLvl5) AS maxHierarcyIDLvl5
    FROM
    (
      SELECT id, hierarcyIDs
      , substring(hierarcyIDs, 0, len(hierarcyIDs)-charindex('/',
    reverse(hierarcyIDs),2)+2) AS hierarcyIdLvl1to4
      , reverse(substring(reverse(hierarcyIDs),2,charindex('/',
      reverse(hierarcyIDs),2)-2)) AS hierarcyIDLvl5
    FROM YourTable
    ) q
    GROUP BY hierarcyIdLvl1to4
    
    GO
    
    hierarcyIdLvl1to4 |最大层次IDLvl5 :---------------- | :---------------- /1/2/3/4/ | 5 /11/12/13/14/ | 42

    db小提琴here

    【讨论】:

      【解决方案3】:

      hierarchyid 的创建者已经预见到您的需求,并为此提供了官方支持的解决方案。

      declare @h table (h HIERARCHYID);
      
      insert into @h (h)
      values 
          ('/1/1/1/6/1/'),
          ('/1/1/1/6/7/'),
          ('/1/1/1/6/3/'),
          ('/1/1/1/6/13/'),
          ('/1/1/1/6/4/');
      
      declare @parent HIERARCHYID = '/1/1/1/6/';
      declare @maxChild HIERARCHYID = (
          select max(h)
          from @h
          where h.IsDescendantOf(@parent) = 1
      );
      
      -- ToString() added here for readability in the output;
      -- it's not needed to be used as data.
      select @parent.GetDescendant(@maxChild, null).ToString();
      

      您可以阅读有关此here 的更多信息。

      解决此问题的另一种方法是您自己将自己的组件指定给hierarchyid。我喜欢使用主键值。例如,假设此数据代表公司的组织结构图。如果 EmployeeID 1 是 CEO,42 是 CFO(向 CEO 汇报),306 是会计经理(向 CFO 汇报),则后者的 hierarchyid 将是/1/42/306/。因为 PK 值是唯一的,所以生成的 hierarchid 也是唯一的。

      【讨论】: