2014-10-13 4 views
1

Itzik Ben-Gan описал здесь несколько простых методов работы с типом данных hierarchyId.Иерархия SQL Server: как добавить несколько дочерних элементов?

http://sqlmag.com/t-sql/hierarchyid

Он использует хранимая процедура для добавления ребенка в какой-то корень

-- Web Listing 2: Script to Create Stored Procedure usp_AddEmp 
IF OBJECT_ID('dbo.usp_AddEmp', 'P') IS NOT NULL 
    DROP PROC dbo.usp_AddEmp; 
GO 
CREATE PROC dbo.usp_AddEmp 
    @empid AS INT, 
    @mgrid AS INT = NULL, 
    @empname AS VARCHAR(25), 
    @salary AS MONEY 
AS 

DECLARE 
    @hid   AS HIERARCHYID, 
    @mgr_hid  AS HIERARCHYID, 
    @last_child_hid AS HIERARCHYID; 

IF @mgrid IS NULL 
    SET @hid = HIERARCHYID::GetRoot(); 
ELSE 
BEGIN 
    SET @mgr_hid = (SELECT hid FROM dbo.Employees WHERE empid = @mgrid); 
    SET @last_child_hid = 
    (SELECT MAX(hid) FROM dbo.Employees 
    WHERE hid.GetAncestor(1) = @mgr_hid); 
    SET @hid = @mgr_hid.GetDescendant(@last_child_hid, NULL); 
END 

INSERT INTO dbo.Employees(empid, hid, empname, salary) 
    VALUES(@empid, @hid, @empname, @salary); 
GO 

Чтобы добавить любой лист к дереву нужно вызвать этот процедурный так:

EXEC dbo.usp_AddEmp 
    @empid = 1, @mgrid = NULL, @empname = 'David' , @salary = $10000.00; 
EXEC dbo.usp_AddEmp 
    @empid = 2, @mgrid = 1, @empname = 'Eitan' , @salary = $7000.00; 
EXEC dbo.usp_AddEmp 
    @empid = 3, @mgrid = 1, @empname = 'Ina' , @salary = $7500.00; 
EXEC dbo.usp_AddEmp 
    @empid = 4, @mgrid = 2, @empname = 'Seraph' , @salary = $5000.00; 
EXEC dbo.usp_AddEmp 
    @empid = 5, @mgrid = 2, @empname = 'Jiru' , @salary = $5500.00; 
EXEC dbo.usp_AddEmp 
    @empid = 6, @mgrid = 2, @empname = 'Steve' , @salary = $4500.00; 
EXEC dbo.usp_AddEmp 
    @empid = 7, @mgrid = 3, @empname = 'Aaron' , @salary = $5000.00; 
EXEC dbo.usp_AddEmp 
    @empid = 8, @mgrid = 5, @empname = 'Lilach' , @salary = $3500.00; 
EXEC dbo.usp_AddEmp 
    @empid = 9, @mgrid = 7, @empname = 'Rita' , @salary = $3000.00; 
EXEC dbo.usp_AddEmp 
    @empid = 10, @mgrid = 5, @empname = 'Sean' , @salary = $3000.00; 
EXEC dbo.usp_AddEmp 
    @empid = 11, @mgrid = 7, @empname = 'Gabriel', @salary = $3000.00; 
EXEC dbo.usp_AddEmp 
    @empid = 12, @mgrid = 9, @empname = 'Emilia' , @salary = $2000.00; 
EXEC dbo.usp_AddEmp 
    @empid = 13, @mgrid = 9, @empname = 'Michael', @salary = $2000.00; 
EXEC dbo.usp_AddEmp 
    @empid = 14, @mgrid = 9, @empname = 'Didi' , @salary = $1500.00; 

Мой вопрос есть:

У меня есть таблица со списком братьев-братьев h Мне нужно добавить к родительскому 14 на том же уровне

declare @toadd table 
(
    empid INT, 
    mgrid INT, 
    empname VARCHAR(25), 
    salary MONEY 
) 

insert into @toadd(empid, mgrid, empname, salary) 
select 15, 14, 'Itzik', $0.01 
union all 
select 16, 14, 'Jack', $0.02 
union all 
select 17, 14, 'Alex', $0.03 
union all 
select 18, 14, 'Mary', $0.04 
union all 
select 19, 14, 'John', $0.05 

Я могу создать курсор и вызвать proc N раз. Но, возможно, это можно сделать более эффективным? Как-то в одном запросе?

ответ

3

Вы можете использовать тот факт, что строка формы '/ [int /] [... n]' будет правильно преобразовываться в HierarchyId. Поэтому вместо того, чтобы использовать GetDescendant для добавления подчиненного менеджера, возьмите HierarchyId менеджера, вызовите ToString() на нем, примените к нему «{EmpId} /» и верните его обратно в HierarchyId (или просто сохраните его, если колонка уже этот типа А именно:.

with emps_and_mgrs as (
    select *, m.hid as mhid 
    from @toadd as t 
    cross apply (
     select hid 
     from dbo.Employees 
     where empid= t.mgrid 
    ) as m 
) 

INSERT INTO dbo.Employees(empid, hid, empname, salary) 
select empid, mhid.ToString() + cast(empid as varchar) + '/', empname, salary 
from emps_and_mgrs; 

Это предполагает, что спрятанное значение каждого имеет вида «{спрятал менеджер)/EmpID /». Это включает Дэвида в оригинальном примере; вместо того, чтобы иметь значение GetRoot() (т. е. «/»), у него будет значение «/ 1 /». Также обратите внимание, что если вы собираетесь принять эту стратегию, я бы также сохранил в команде управляющего. Таким образом, вы можете написать рекурсивный цит, который использует только empid и управляющего, чтобы восстановить все Иерархические идентификаторы, если что-то идет вбок.

+0

Thanx Ben, в моем реальном db Я храню id, parent_id в таблице. – Oleg

+0

Но ... что, если заменить 14 на 5? То есть 5 уже есть некоторые дети и нужно добавить эти новые 5 вправо от существующего «брата»? – Oleg

+0

Это не проблема. Поскольку EmpId уникален, каждая из соответствующих иерархий будет уникальной. Так, например, запись с EmpId = 15 имела бы иерархию/1/2/5/15 /, а EmpId = 16 имела бы/1/2/5/16 /. Ранее существовавшая строка для EmpId = 8 имела бы/1/2/5/8 /, которая отличается от любой другой. –