Этот баннер — требование Роскомнадзора для исполнения 152 ФЗ.
«На сайте осуществляется обработка файлов cookie, необходимых для работы сайта, а также для анализа использования сайта и улучшения предоставляемых сервисов с использованием метрической программы Яндекс.Метрика. Продолжая использовать сайт, вы даёте согласие с использованием данных технологий».
Политика конфиденциальности
|
|
|
Удаление дерева
|
|||
|---|---|---|---|
|
#18+
Может кто сталкивался? Совсем голову сломал. ID ROOT NAME 1 0 A 2 1 B 3 1 C 4 3 D 5 0 Е ... Возможно ли удалив 1(А) удалить то что от него растет те 2,3,4 ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 17.05.2001, 15:01 |
|
||
|
Удаление дерева
|
|||
|---|---|---|---|
|
#18+
В SQL 7.0 каскадных constraint'ов не было. В 2К появились, но не позволяют создать foreign key c каскадным удалением/апдейтом на ту же самую таблицу (на другую - пожалуйста). Так что средствами дизайна базы этого нормально не сделаешь. Только писать скрипт, который будет вручную это делать... ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 17.05.2001, 15:06 |
|
||
|
Удаление дерева
|
|||
|---|---|---|---|
|
#18+
Писал с головы "на прямую". Вроде, должно работать. CREATE PROCEDURE DeleteTree_Proc (ID int) AS CREATE TABLE #Tmp1 (ID int, Group bit) DECLARE @Group bit DECLARE @Flag int SET @Group=0 SET @Flag=1 INSERT INTO #Tmp1 (ID,Group ) VALUES(ID,@Group) WHILE @Flag>0 BEGIN -------------------------------------1 SET @Flag=(SELECT COUNT(*) FROM TreeTable T, #Tmp1 Tm WHERE Tm.Group=@Group AND Tm.ID=T.ROOT) INSERT INTO #Tmp1 (ID,Group) SELECT T.ID, ABS(@Group-1) FROM TreeTable T, #Tmp1 Tm WHERE Tm.Group=@Group AND Tm.ID=T.ROOT DELETE TreeTable FROM TreeTable T, #Tmp1 Tm WHERE Tm.Group=@Group AND Tm.ID=T.ID DELETE #Tmp1 WHERE Group=@Group SET @Group=ABS(@Group-1) END --------------------------------------2 ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 17.05.2001, 16:48 |
|
||
|
Удаление дерева
|
|||
|---|---|---|---|
|
#18+
Упс, обшибочки увидел, должно быть так: CREATE PROCEDURE DeleteTree_Proc (@ID int) AS CREATE TABLE #Tmp1 (ID int, Grp bit) DECLARE @Group bit DECLARE @Flag int SET @Group=0 SET @Flag=1 INSERT INTO #Tmp1 (ID,Grp) VALUES(@ID,@Group) WHILE @Flag>0 BEGIN -------------------------------------1 SET @Flag=(SELECT COUNT(*) FROM TreeTable T, #Tmp1 Tm WHERE Tm.Grp=@Group AND Tm.ID=T.ROOT) INSERT INTO #Tmp1 (ID,Grp) SELECT T.ID, ABS(@Group-1) FROM TreeTable T, #Tmp1 Tm WHERE Tm.Grp=@Group AND Tm.ID=T.ROOT DELETE TreeTable FROM TreeTable T, #Tmp1 Tm WHERE Tm.Grp=@Group AND Tm.ID=T.ID DELETE #Tmp1 WHERE Grp=@Group SET @Group=ABS(@Group-1) END --------------------------------------2 ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 17.05.2001, 16:58 |
|
||
|
Удаление дерева
|
|||
|---|---|---|---|
|
#18+
1. Разрешаешь выполнение рекурисвных триггеров в параметрах базы данных 2. Создаешь рекурсивный триггер: create trigger TreeDel on MyTable for Delete as if exists(select M.ID from MyTable M, Deleted D where M.Root=D.ID) -- Условие прекращения рекурсии delete from MyTable from MyTable M, deleted D where M.Root=D.ID И все! В SQL 7.0 будет работать с деревьями глубиной до 16 уровней вложенности, в SQL2000 - глубиной до 32 уровней. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 17.05.2001, 18:51 |
|
||
|
Удаление дерева
|
|||
|---|---|---|---|
|
#18+
Господа всем спасибо большое за помощь! Что-то сляпал сам. Кажется работает Alter Procedure SP_DEL_GEO @IDX_GEO as int As declare @ROOT as int if exists(select IDX_GEO from GEOS where IDX_GEO =@IDX_GEO) begin delete from GEOS where IDX_GEO=@IDX_GEO while exists(select IDX_GEO from GEOS where ROOT=@IDX_GEO) begin set @ROOT=(select max(IDX_GEO) from GEOS where ROOT=@IDX_GEO) exec SP_DEL_GEO @ROOT continue end end ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.06.2001, 07:40 |
|
||
|
Удаление дерева
|
|||
|---|---|---|---|
|
#18+
Использование рекурсий, конечно упрощает алгоритм, НО ... Господа, не забывайте про ограничение на вложенность - всего 32, и если теоритически цепочка может иметь больше чем 32 звена - надо изголяться (Законы Мэрфи еще никто не отменял ). 2Игорь: Собственно про алгоритм. Он, конечно, отработает, но слишком затратный - посчитай, сколько раз ты будут выполняться запросы для маленького дерева, имеющего хотябы 10 уровней . Используй курсоры. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 08.06.2001, 06:46 |
|
||
|
Удаление дерева
|
|||
|---|---|---|---|
|
#18+
2 Игорь Я не советую использовать то что Вы написали - как-то ненадежно работает в MS SQL рекурсия. К тому же у Вас получается несколько операций удаления из таблицы, что будет тормозить. Предлагаю свой вариант, это слегка переработанный вариант AnKa: (я не проверял, могут быть ошибки) CREATE PROCEDURE DeleteTree_Proc (@ID int) AS declare @d table(id int, level int) declare @level int set @level=0 insert @d select @ID,0 while exists(select * from TBL t, @d d where id=ROOT and level=@level) begin insert @d select ID, @level+1 from TBL t, @d d where id=ROOT and level=@level set @level=@level+1 end delete TBL from TBL, @d where id=ID С приветом Сергей PS. Если уж здесь использовать курсоры, то может вообще тогда на Фоксе писать? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 08.06.2001, 07:56 |
|
||
|
Удаление дерева
|
|||
|---|---|---|---|
|
#18+
2 Игорь Я не советую использовать то что Вы написали - как-то ненадежно работает в MS SQL рекурсия. К тому же у Вас получается несколько операций удаления из таблицы, что будет тормозить. Предлагаю свой вариант, это слегка переработанный вариант AnKa: (я не проверял, могут быть ошибки) CREATE PROCEDURE DeleteTree_Proc (@ID int) AS declare @d table(id int, level int) declare @level int set @level=0 insert @d select @ID,0 while exists(select * from TBL t, @d d where id=ROOT and level=@level) begin insert @d select ID, @level+1 from TBL t, @d d where id=ROOT and level=@level set @level=@level+1 end delete TBL from TBL, @d where id=ID С приветом Сергей PS. Если уж здесь использовать курсоры, то может вообще тогда на Фоксе писать? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 08.06.2001, 07:58 |
|
||
|
Удаление дерева
|
|||
|---|---|---|---|
|
#18+
Добрый день! Удаление узла вместе с дочерними узлами можно выполнять и без рекурсии, и без курсоров. Пример итеративного подхода, на реальной БД (AnAccountAndItsAncestors - служебная таблица для раскрутки иерархий): CREATE VIEW akViewAccountLevel AS SELECT AccountId, COUNT(AncestorId) AS AccountLevel FROM AnAccountAndItsAncestors GROUP BY AccountId GO SET QUOTED_IDENTIFIER ON GO CREATE PROCEDURE akDropCustomAccount @AccountId AnAccountId, @CheckRights BIT = 1 AS BEGIN /* initialize execution state */ DECLARE @Result INT SET NOCOUNT ON SET @Result = 0 BEGIN TRANSACTION IF (@CheckRights = 1) AND (dbo.akHasConstraint('Accounting:Plan:Change') = 0) BEGIN SET @Result = -101 GOTO fin END /* drop children and self - one level per iteration */ WHILE EXISTS(SELECT TOP 1 * FROM AnAccountAndItsAncestors WHERE (AncestorId = @AccountId)) BEGIN /* one step is dropping of children which nesting level is maximal */ DELETE FROM ACustomAccount WHERE "Id" IN ( SELECT A1.AccountId FROM AnAccountAndItsAncestors A1 INNER JOIN akViewAccountLevel AL1 ON (AL1.AccountId = A1.AccountId) WHERE (A1.AncestorId = @AccountId) AND ( AL1.AccountLevel = ( SELECT MAX(AL2.AccountLevel) FROM akViewAccountLevel AL2 INNER JOIN AnAccountAndItsAncestors A2 ON (AL2.AccountId = A2.AccountId) WHERE (A2.AncestorId = @AccountId) ) ) ) SET @Result = @@ERROR IF @Result <> 0 GOTO fin END /* finalize transaction */ fin: IF @@TRANCOUNT > 0 BEGIN IF @Result <> 0 ROLLBACK TRANSACTION ELSE COMMIT TRANSACTION END RETURN @Result END GO А вот реальная схема БД: if exists (select * from sysobjects where id = object_id('"ACustomAccount"') and sysstat & 0xf = 3) drop table "ACustomAccount" GO CREATE TABLE "ACustomAccount" ( "Id" "AnAccountId" IDENTITY (1, 1) NOT NULL , "IsRoot" "AnUnorderedBoolean" NOT NULL CONSTRAINT "Set_to_false14" DEFAULT (0), "IsSub" "AnUnorderedBoolean" NOT NULL CONSTRAINT "Set_to_false15" DEFAULT (0), "IsFolder" "AnUnorderedBoolean" NOT NULL CONSTRAINT "Set_to_false16" DEFAULT (0), "IsLeaf" "AnUnorderedBoolean" NOT NULL CONSTRAINT "Set_to_false17" DEFAULT (0), "IsInClientBalance" "AnUnorderedBoolean" NOT NULL CONSTRAINT "Set_to_false18" DEFAULT (0), CONSTRAINT "XPKACustomAccount" PRIMARY KEY CLUSTERED ( "Id" ) WITH FILLFACTOR = 90 ) GO if exists (select * from sysobjects where id = object_id('"AFolderAccount"') and sysstat & 0xf = 3) drop table "AFolderAccount" GO CREATE TABLE "AFolderAccount" ( "Id" "int" NOT NULL , CONSTRAINT "XPKAFolderAccount" PRIMARY KEY CLUSTERED ( "Id" ) WITH FILLFACTOR = 90, CONSTRAINT "aca_is_afa" FOREIGN KEY ( "Id" ) REFERENCES "ACustomAccount" ( "Id" ) ON DELETE CASCADE ) GO if exists (select * from sysobjects where id = object_id('"ASubAccount"') and sysstat & 0xf = 3) drop table "ASubAccount" GO CREATE TABLE "ASubAccount" ( "Id" "int" NOT NULL , "FolderAccountId" "int" NOT NULL , "Code" "AnAccountCode" NOT NULL , "Path" "varchar" (8000) COLLATE SQL_Latin1_General_CP1251_CI_AS NOT NULL , CONSTRAINT "XPKASubAccount" PRIMARY KEY NONCLUSTERED ( "Id" ) WITH FILLFACTOR = 90, CONSTRAINT "XAKASubAccountPath" UNIQUE NONCLUSTERED ( "Path" ) WITH FILLFACTOR = 90, CONSTRAINT "XAKSubAccountCode" UNIQUE NONCLUSTERED ( "FolderAccountId", "Code" ) WITH FILLFACTOR = 90, CONSTRAINT "aca_is_asa" FOREIGN KEY ( "Id" ) REFERENCES "ACustomAccount" ( "Id" ) ON DELETE CASCADE , CONSTRAINT "contain_subaccounts" FOREIGN KEY ( "FolderAccountId" ) REFERENCES "AFolderAccount" ( "Id" ), CONSTRAINT "IsValidAccountCode74" CHECK (not([Code] like '%\%')) ) GO if exists (select * from sysobjects where id = object_id('"AnAccountAndItsAncestors"') and sysstat & 0xf = 3) drop table "AnAccountAndItsAncestors" GO CREATE TABLE "AnAccountAndItsAncestors" ( "AncestorId" "int" NOT NULL , "AccountId" "int" NOT NULL , CONSTRAINT "XPKAnAccountAndItsAncestors" PRIMARY KEY CLUSTERED ( "AncestorId", "AccountId" ) WITH FILLFACTOR = 90, CONSTRAINT "account_under_ancestors" FOREIGN KEY ( "AccountId" ) REFERENCES "ACustomAccount" ( "Id" ) ON DELETE CASCADE , CONSTRAINT "is_ancestor_of" FOREIGN KEY ( "AncestorId" ) REFERENCES "ACustomAccount" ( "Id" ) ) GO SET QUOTED_IDENTIFIER ON GO SET ANSI_NULLS ON GO if exists (select * from sysobjects where id = object_id('"dbo"."akCreateACustomAccount"') and sysstat & 0xf = 4) drop procedure "dbo"."akCreateACustomAccount" GO CREATE PROCEDURE akCreateACustomAccount @Id AnAccountId OUTPUT, @CheckRights BIT = 1 AS BEGIN /* initialize execution state */ DECLARE @Result INT SET NOCOUNT ON SET @Result = 0 SET @Id = -1 BEGIN TRANSACTION IF (@CheckRights = 1) AND (dbo.akHasConstraint('Accounting:Plan:Change') = 0) BEGIN SET @Result = -101 GOTO fin END INSERT INTO ACustomAccount DEFAULT VALUES SET @Result = @@ERROR IF @Result = 0 BEGIN SET @Id = @@IDENTITY INSERT INTO AnAccountAndItsAncestors(AccountId, AncestorId) VALUES(@Id, @Id) SET @Result = @@ERROR END /* finalize transaction */ fin: IF @@TRANCOUNT > 0 BEGIN IF @Result <> 0 ROLLBACK TRANSACTION ELSE COMMIT TRANSACTION END RETURN @Result END SET QUOTED_IDENTIFIER ON GO SET QUOTED_IDENTIFIER OFF GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_NULLS ON GO ALTER PROCEDURE akCreateAFolderAccount @Id AnAccountId OUTPUT, @CheckRights BIT = 1 AS BEGIN /* initialize execution state */ DECLARE @Result INT SET NOCOUNT ON SET @Result = 0 BEGIN TRANSACTION EXECUTE @Result = akCreateACustomAccount @Id OUTPUT, @CheckRights IF @Result = 0 BEGIN INSERT INTO AFolderAccount("Id") VALUES(@Id) SET @Result = @@ERROR END /* finalize transaction */ fin: IF @@TRANCOUNT > 0 BEGIN IF @Result <> 0 ROLLBACK TRANSACTION ELSE COMMIT TRANSACTION END RETURN @Result END GO SET QUOTED_IDENTIFIER OFF GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_NULLS ON GO ALTER PROCEDURE akCreateASubAccount @OwnerFolderId AnAccountId, @Code AnAccountCode, @Id AnAccountId OUTPUT, @CheckRights BIT = 1 AS BEGIN DECLARE @Path VARCHAR(8000) /* initialize execution state */ DECLARE @Result INT SET NOCOUNT ON SET @Result = 0 BEGIN TRANSACTION EXECUTE @Result = akCreateAnAccount @Id OUTPUT, @CheckRights IF @Result = 0 BEGIN SET @Path = dbo.akGetInternalAccountPath(@OwnerFolderId, @Code) INSERT INTO ASubAccount("Id", "Code", "FolderAccountId", "Path") VALUES(@Id, @Code, @OwnerFolderId, @Path) SET @Result = @@ERROR IF @Result <> 0 GOTO fin EXECUTE @Result = akUpdateSingleSubAccountAncestors @Id END /* finalize transaction */ fin: IF @@TRANCOUNT > 0 BEGIN IF @Result <> 0 ROLLBACK TRANSACTION ELSE COMMIT TRANSACTION END RETURN @Result END GO SET QUOTED_IDENTIFIER OFF GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_NULLS ON GO ALTER PROCEDURE akUpdateSingleSubAccountAncestors @AccountId AnAccountId AS BEGIN /* initialize execution state */ DECLARE @Result INT SET NOCOUNT ON SET @Result = 0 BEGIN TRANSACTION INSERT INTO AnAccountAndItsAncestors(AccountId, AncestorId) SELECT @AccountId, AAIA.AncestorId FROM AnAccountAndItsAncestors AAIA INNER JOIN ASubAccount SA ON (AAIA.AccountId = SA.FolderAccountId) WHERE SA."Id" = @AccountId SET @Result = @@ERROR IF @Result <> 0 GOTO fin /* finalize transaction */ fin: IF @@TRANCOUNT > 0 BEGIN IF @Result <> 0 ROLLBACK TRANSACTION ELSE COMMIT TRANSACTION END RETURN @Result END GO SET QUOTED_IDENTIFIER OFF GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_NULLS ON GO if exists (select * from dbo.sysobjects where id = object_id('"dbo"."akGetInternalAccountPath"') and xtype in ('FN', 'IF', 'TF')) drop function "dbo"."akGetInternalAccountPath" GO CREATE FUNCTION akGetInternalAccountPath(@FolderAccountId AnAccountId, @AccountCode AnAccountCode) RETURNS VARCHAR(8000) AS BEGIN DECLARE @Result VARCHAR(8000) SET @Result = dbo.akGetAccountPath(@FolderAccountId) + '\' + @AccountCode RETURN @Result END GO SET QUOTED_IDENTIFIER OFF GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_NULLS ON GO if exists (select * from sysobjects where id = object_id('"dbo"."akCreateAnAccount"') and sysstat & 0xf = 4) drop procedure "dbo"."akCreateAnAccount" GO CREATE PROCEDURE akCreateAnAccount @Id AnAccountId OUTPUT, @CheckRights BIT = 1 AS BEGIN /* initialize execution state */ DECLARE @Result INT SET NOCOUNT ON SET @Result = 0 BEGIN TRANSACTION EXECUTE @Result = akCreateACustomAccount @Id OUTPUT, @CheckRights IF @Result = 0 BEGIN INSERT INTO AnAccount("Id") VALUES(@Id) SET @Result = @@ERROR END /* finalize transaction */ fin: IF @@TRANCOUNT > 0 BEGIN IF @Result <> 0 ROLLBACK TRANSACTION ELSE COMMIT TRANSACTION END RETURN @Result END GO SET QUOTED_IDENTIFIER OFF GO SET ANSI_NULLS ON GO if exists (select * from sysobjects where id = object_id('"AnAccount"') and sysstat & 0xf = 3) drop table "AnAccount" GO CREATE TABLE "AnAccount" ( "Id" "int" NOT NULL , CONSTRAINT "XPKAnAccount" PRIMARY KEY CLUSTERED ( "Id" ) WITH FILLFACTOR = 90, CONSTRAINT "aca_is_aa" FOREIGN KEY ( "Id" ) REFERENCES "ACustomAccount" ( "Id" ) ON DELETE CASCADE ) GO Я постарался отобрать самый минимум ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 14.06.2001, 15:02 |
|
||
|
|

start [/forum/topic.php?fid=46&fpage=3569&tid=1826494]: |
0ms |
get settings: |
10ms |
get forum list: |
19ms |
check forum access: |
3ms |
check topic access: |
3ms |
track hit: |
30ms |
get topic data: |
10ms |
get forum data: |
2ms |
get page messages: |
53ms |
get tp. blocked users: |
1ms |
| others: | 272ms |
| total: | 403ms |

| 0 / 0 |
