"Tonal" <[EMAIL PROTECTED]> сообщил/сообщила в
новостях следующее: news:[EMAIL PROTECTED]
>
...
> гарантировать, что в нашем дереве нет циклов так просто не получится...
:-(
...

Как так не получится?
Получается элементарными триггерами.
С 2002 года работает и не жужжит.
:-)
Наверное не очень красиво.
Но это писАлось очень давно, аж 6 лет назад.

CREATE TABLE DM_GR (
    ID         INTEGER NOT NULL,
    PARENT_ID  INTEGER,
    NAME       VARCHAR(63) COLLATE PXW_CYRL,
    POSTSCRIB  BLOB SUB_TYPE 1 SEGMENT SIZE 80
);

ALTER TABLE DM_GR ADD CONSTRAINT DM_GR_PK PRIMARY KEY (ID);

ALTER TABLE DM_GR ADD CONSTRAINT DM_GR_DM_GR FOREIGN KEY (PARENT_ID)
REFERENCES DM_GR (ID) ON DELETE NOACTION ON UPDATE CASCADE;

CREATE OR ALTER TRIGGER DM_GR_BIU20 FOR DM_GR
ACTIVE BEFORE INSERT OR UPDATE POSITION 20
AS
DECLARE VARIABLE ISCYRCLE INTEGER;
begin
/* Проверка на цикличность
  (выбрасывает, если новый ID соответствует ID одного из предков) */
   if (New.ID = New.Parent_ID) then
      execute procedure err_showmessage('Узел не может содержать сам себя');
   execute procedure DM_GR_Cyrc(New.Id,New.Parent_Id) returning_values
IsCyrcle;
   if (IsCyrcle = 1) then
     execute procedure err_showmessage('Узел не может содержать сам себя');
end

CREATE OR ALTER PROCEDURE DM_GR_CYRC(
    ID INTEGER,
    PARENT_ID INTEGER)
RETURNS (
    RESULT INTEGER)
AS
DECLARE VARIABLE NEWPARENT_ID INTEGER;
begin
/*  Проверка на цикличность.
  Проверяет соответствует ли ID новой записи
  ID одного из родителей старше первого.
  Возвращает 0 или 1. */
  for select DM_Gr.Parent_ID from DM_GR
    where DM_GR.ID = :Parent_ID
    into :NewParent_ID
  do
    begin
      Result = 0;
      if (:NewParent_ID = :ID) then
        begin
          Result = 1;
          exit;
        end
      else execute procedure DM_GR_CYRC :ID, :NewParent_ID returning_values
:Result;
      exit;
    end
end

-- 
Галимов Артур Амирзянович.
"ФармМедСервис" (Сочи).


Ответить