SQL Split - вставить в иерархическую структуру таблицы - PullRequest
3 голосов
/ 13 января 2011

Я использую функцию Split (найденную на 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 (называемым GetId с параметром @FullName) - 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)

В результате я не хочусгенерируйте значение для столбца Id в таблице Category и необходимо получить соответствующий 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 Ответ

3 голосов
/ 13 января 2011

Вы можете использовать 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

Теперь для 2-й части вставляется вся иерархия, но начинается с 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...