SQL拆分 - 插入分层表结构

时间:2011-01-12 23:57:32

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

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

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

我得到以下

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

其中 Id 只是表示原始字符串中位置的序号,名称是该节点的名称。还没有分层信息。

现在,下一步是将其放入DB中的分层数据结构中。我试图在存储过程中执行此操作,并且我的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(使用param @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

Q :是否可以从最外层节点开始递归地回调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)
}

1 个答案:

答案 0 :(得分:3)

您可以使用CTE与RowNumbering结合使用

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