【问题标题】:SQL Split - insert into hierarchical table structureSQL Split - 插入分层表结构
【发布时间】:2011-01-12 23:57:31
【问题描述】:

我正在使用拆分功能(在 social.msdn.com 上找到)并且在查询窗口中手动执行时

SELECT * FROM dbo.Split('/ABC/DEF/GHI/JKL', '/')  

我得到以下信息

Id  Name
--  ----
 1
 2   ABC
 3   DEF
 4   GHI
 5   JKL

Id 只是一个序号,表示在原始字符串中的位置,Name 是该节点的名称。尚无分层信息。

现在,下一步是将其放入数据库中的分层数据结构中。我正在尝试在存储过程中执行此操作,并且我的 SQL 技能就是这样,我碰壁了。以下是我希望拥有的内容:(请注意,上面的 Id 列与此处的 Id 或 ParentId 列无关。)

Id  ParentId  Name  FullName
--  --------  ----  --------
1     NULL     ABC   /ABC
2      1       DEF   /ABC/DEF
3      2       GHI   /ABC/DEF/GHI
4      3       JKL   /ABC/DEF/GHI/JKL

我的 SP(使用参数 @FullName 称为 GetId)已经到此为止了 - GetId 应该返回与此节点关联的 Id。如果节点不存在,则应创建该节点并返回该新行的 Id - 换句话说,此 SP 的使用者不应该关心或知道节点在调用它之前是否存在:

DECLARE @count int

-- // is there already a row for this node?
SELECT @count = COUNT(CatId)
FROM Category
WHERE FullName = @FullName

-- // if no row for this node, create the row
-- // and perhaps create multiple rows in hierarchy up to root
IF (@count = 0)
BEGIN
    SELECT * FROM Split(@FullName, '/')
    -- // NOW WHAT ???
    -- // need to insert row (and perhaps parents up to root)
END

-- // at this point, there should be a row for this node
-- // return the Id associated with this node
SELECT Id
FROM Category
WHERE FullName = @FullName

通过一系列插入,这些项目最终将结束的类别表(邻接表)具有以下结构。

CREATE TABLE Category (
    Id int IDENTITY(1,1) NOT NULL PRIMARY KEY,
    ParentId int NULL,
    Name nvarchar(255) NOT NULL,
    FullName nvarchar(255) NOT NULL)

因此,我不想为 Category 表中的 Id 列生成值,并且需要为每个节点获取适当的 ParentId。

在处理路径“/ABC/DEF/GHI/JKL”和“/ABC/DEF/XYZ/LMN/OPQ”并执行 SELECT * FROM Category 之后,我希望看到以下内容:

Id  ParentId  Name  FullName
--  --------  ----  --------
1     NULL     ABC   /ABC
2      1       DEF   /ABC/DEF
3      2       GHI   /ABC/DEF/GHI
4      3       JKL   /ABC/DEF/GHI
5      2       XYZ   /ABC/DEF/XYZ
6      5       LMN   /ABC/DEF/XYZ/LMN
7      6       OPQ   /ABC/DEF/XYZ/LMN/OPQ

问:是否可以从最外层节点开始递归回调 SP,直到该节点存在或我们位于最终父节点?大意是:

GetId(@FullName)
{
If Category exists with @FullName
    return CatId
Else  // row doesn't exist for this node
    Split @FullName, order by Id DESC so we get the leaf node first
    Create Category row
      @FullName,
      @Name,
      @ParentId = Id of next FullName (call GetId with FullName of next row from Split)
}

【问题讨论】:

  • 您使用的是哪个版本的 SQL Server?如果2008年你考虑过hierarchyid吗?
  • 你表示路径的方式让我想起了很多 hierarchyid 格式。我自己并没有真正使用过它,所以不知道它是否比邻接列表模型更适合您的目的......
  • @Martin 我快速浏览了 HierarchyId,它看起来很有趣,但我认为它对我不起作用,因为层次结构信息(路径)具有数字节点,而不是字符串节点。 bit.ly/iek63I

标签: sql sql-server tsql stored-procedures string


【解决方案1】:

您可以结合 RowNumbering 使用 CTE 来实现此目的

With TMP AS (
    SELECT Id, Data as Name, RN=ROW_NUMBER() over (Order by Id ASC)
    FROM dbo.Split('/ABC/DEF/GHI/JKL', '/')
    where Data > ''
), TMP2 AS (
    SELECT TOP 1 RN, CONVERT(bigint, null) ParentId, Name, convert(nvarchar(max),'/' + Name) FullName
    From TMP
    Order by RN
    union all
    SELECT n.RN, t.RN, n.Name, t.FullName + '/' + n.Name
    from TMP2 t
    inner join TMP n on n.RN = t.RN+1)
select *
from tmp2
order by RN

现在对于第二部分,这将插入整个层次结构,但从 ID=1 开始

IF (@count = 0)
BEGIN
    With TMP AS (
        SELECT Id, Data as Name, RN=ROW_NUMBER() over (Order by Id ASC)
        FROM dbo.Split('/ABC/DEF/GHI/JKL', '/')
        where Data > ''
    ), TMP2 AS (
        SELECT TOP 1 RN, CONVERT(bigint, null) ParentId, Name, convert(nvarchar(max),'/' + Name) FullName
        From TMP
        Order by RN
        union all
        SELECT n.RN, t.RN, n.Name, t.FullName + '/' + n.Name
        from TMP2 t
        inner join TMP n on n.RN = t.RN+1)
    insert Category(CatId, ParentId, Name, FullName)  --<< list correct column names
    select RN, ParentId, Name, FullName
    from tmp2
    order by RN
END

【讨论】:

  • 我还不确定你想对第二部分做什么。最终(重新)构建的 FullName 无疑会匹配输入 @FullName,那么拆分的目的是什么?
  • @cyberwiki 我想做一系列插入,将层次结构生成到邻接列表数据库模式中。我不确定上述解决方案如何适合插入场景。
  • @Ed.S.仍然不是 100% 确定。您想测试 full 名称是否存在,然后如果它没有插入整个树(其中的一部分可能已经存在,例如 /ABC 很可能存在于其他地方)。 ID 将进入身份列,因此 parentID 需要调整?
  • @cyberwiki 用于插入邻接列表表,可能不存在或某些树结构已存在,因此我需要根据需要插入。例如,第一个完整路径是“/ABC/DEF/GHI/JKL”,产生 4 个新记录,id 分别为 1、2、3 和 4。第二个完整路径是“/ABC/DEF/XYZ/LMN/OPQ”,其中root 和 root+1 已经存在,但需要插入 3 个新记录,id 为 5、6 和 7。我包含 FullName 的主要原因是在查找节点时提高 SELECT 效率。
猜你喜欢
  • 2020-11-30
  • 1970-01-01
  • 2021-01-10
  • 1970-01-01
  • 2019-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多