powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Решение по перемещению узлов в таблице вложенных множеств
1 сообщений из 1, страница 1 из 1
Решение по перемещению узлов в таблице вложенных множеств
    #32031482
Alex_Jou
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
-- Предлагаю решение - хранимую процедуру p_dlj_move для перемещения/переподчинения
-- узла+подчинённых ему узлов в таблице вложенных множеств (дереве)

-- Структура иерархической таблицы t_dolj:
-- id_dolj INT -- ID записи
-- lft_dolj INT -- Левая граница множества
-- rgt_dolj INT -- Правая граница множества
-- klfn_name CHAR(30) -- имя узла
-- del_ TINYINT -- 0-запись активна 1-запись помечена на удаление

IF EXISTS (SELECT name FROM sysobjects WHERE name = 'p_dlj_move' AND type = 'P')
DROP PROCEDURE p_dlj_move
GO

CREATE PROCEDURE [dbo].[p_dlj_move]
@Id_which INT = NULL, -- ID перемещаемого пункта
@id_target INT = NULL -- ID пункта назначения (к кому подключение)
AS
SET NOCOUNT ON

IF @Id_which IS NULL
BEGIN
RAISERROR (' Должно быть обязательно указано ID перемещаемого пункта!',16,1)
RETURN
END
IF @id_target IS NULL
BEGIN
RAISERROR (' Должно быть обязательно указано ID пункта назначения!',16,1)
RETURN
END

DECLARE @l_List BIT, @d_lft INT, @d_rgt INT, @cnt_Ins INT, @right_most_sibling INT,
@transcount INT
SET @l_List=0
SET @transcount=@@TRANCOUNT
IF @transcount=0
BEGIN TRANSACTION mytran12
ELSE
SAVE TRAN mytran12

IF EXISTS -- проверяем не есть ли попытка переместить пункт внутрь самого себя
-- (т.е. подчинить себя своим же подчинённым)
(SELECT * FROM t_dolj WITH (TABLOCKX HOLDLOCK) -- Полная блокировка таблицы до конца транзакции!!!
WHERE id_dolj=@id_target
AND id_dolj IN
(SELECT p1.id_dolj -- выбираем все подчинённые перемещаемому пункту записи
FROM t_dolj AS p1, t_dolj AS p2
WHERE p2.lft_dolj+1<=p1.lft_dolj AND p1.lft_dolj<=p2.rgt_dolj
AND p2.id_dolj = @Id_which AND p1.del_=0))
BEGIN
ROLLBACK TRAN mytran12
RAISERROR (' Недопустимо перемещать пункт самого в себя !',16,1)
RETURN
END

IF EXISTS -- проверяем: не есть ли бестолковая попытка снова подчинить перемещаемый пункт
-- своему же непосредственному "начальнику"
(SELECT * FROM t_dolj
WHERE id_dolj=@id_target
AND id_dolj IN
(SELECT MAX(p2.id_dolj) AS id_boss -- выбираем запись-"непосредственного начальника" перемещаемого пункта
FROM t_dolj AS p1, t_dolj AS p2
WHERE p2.lft_dolj<p1.lft_dolj AND p1.lft_dolj<p2.rgt_dolj
AND p1.id_dolj = @Id_which AND p2.del_=0))
BEGIN
ROLLBACK TRAN mytran12
RAISERROR (' Данный пункт уже непосредственно "подчиняется" указанному !',16,1)
RETURN
END

IF EXISTS(
-- проверяем: является ли активным листом узел, к которому намечается подключение
SELECT * FROM t_dolj
WHERE id_dolj=@id_target AND del_=0 AND id_dolj IN
-- выбираем все активные листья:
(SELECT id_dolj FROM t_dolj WHERE lft_dolj=rgt_dolj-1 AND del_=0))
SET @l_List=1

IF @l_List=1
BEGIN
-- Проверки: есть ли в других таблицах ссылки на узел-лист, к которому намечается
-- прикрепить в подчинение перемещаемый пункт. Если да, то ОТКАТ!
......
......
END

-- Запоминаем границы перемещаемого пункта:
SELECT @d_lft=lft_dolj, @d_rgt=rgt_dolj FROM t_dolj WHERE id_dolj=@Id_which

-- Помечаем на удаление перемещаемый пункт и его подчинённые записи
UPDATE t_dolj SET del_=2 WHERE lft_dolj BETWEEN @d_lft AND @d_rgt
SET @cnt_Ins=@@ROWCOUNT -- эту команду сразу за предыдущей!
IF @@ERROR<>0
BEGIN
ROLLBACK TRAN mytran12
RAISERROR (' При переносе узла: ошибка пометки на удаление перемещаемого(ых) пункта(ов)!',16,1)
RETURN
END

-- Уплотняем промежутки после "удаления"
UPDATE t_dolj SET
lft_dolj = CASE WHEN lft_dolj>@d_lft THEN lft_dolj-(@d_rgt-@d_lft+1) ELSE lft_dolj END,
rgt_dolj = CASE WHEN rgt_dolj>@d_lft THEN rgt_dolj-(@d_rgt-@d_lft+1) ELSE rgt_dolj END
WHERE del_=0
IF @@ERROR<>0
BEGIN
ROLLBACK TRAN mytran12
RAISERROR (' При переносе узла: ошибка уплотнения дерева!',16,1)
RETURN
END

-- Выясняем правую границу узла, к которому присоединение, после уплотнения
SET @right_most_sibling =(SELECT rgt_dolj FROM t_dolj
WHERE id_dolj = @id_target AND del_=0)

-- Регенерация дерева с учётом последующего восстановления перемещаемых пунктов
UPDATE t_dolj
SET lft_dolj = CASE WHEN lft_dolj > @right_most_sibling THEN lft_dolj+@cnt_Ins*2 ELSE lft_dolj END,
rgt_dolj = CASE WHEN rgt_dolj >= @right_most_sibling THEN rgt_dolj+@cnt_Ins*2 ELSE rgt_dolj END
WHERE rgt_dolj >= @right_most_sibling AND del_=0
IF @@ERROR<>0
BEGIN
ROLLBACK TRAN mytran12
RAISERROR (' При переносе узла: ошибка регенерации дерева!',16,1)
RETURN
END

-- "восстанавливаем" перемещаемые пункты, подчиняя новому узлу
UPDATE t_dolj
SET lft_dolj=@right_most_sibling+lft_dolj-@d_lft,
rgt_dolj=@right_most_sibling+@cnt_Ins*2-1-@d_rgt+rgt_dolj, del_=0
WHERE del_=2
IF @@ERROR=0
BEGIN IF @transcount=0 COMMIT TRANSACTION END
ELSE
BEGIN
ROLLBACK TRAN mytran12
RAISERROR (' При переносе узла: ошибка восстановления перемещаемого(ых) пукта(ов) в указанном месте!',16,1)
RETURN
END
...
Рейтинг: 0 / 0
1 сообщений из 1, страница 1 из 1
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Решение по перемещению узлов в таблице вложенных множеств
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]