Древовидная таблица: удаление узла с подузлами

IBX, FIBPlus, UIB, ADO, .Net и прочее-прочее-прочее, в общем все, что относится к созданию приложений, работающих с InterBase, Firebird и Yaffil - клиент-серверных, трехзвенных, консольных и т.п.

Модератор: kdv

Ответить
Solo
Сообщения: 108
Зарегистрирован: 18 апр 2005, 04:05

Древовидная таблица: удаление узла с подузлами

Сообщение Solo » 16 май 2005, 05:01

Добрый день. Такой вопрос: Древовидная таблица в FB (D6, IBO4.5). Принцип прост: PARENTID чилда равен ID родителя. Вложенность не бесконечна, а ограничена 4-мя уровнями: Улицы - Дома - Квартиры - Комнаты.

Вопрос: Как удалить, к примеру, улицу, чтобы удалились ее дома, квартиры этих домов и комнаты в этих квартирах.

Контрол этой таблицы - IBOGrid, датасет этой таблицы - IB_Query (не путать с IBQuery из IBX). Почти такая же штука, как IBDataSet. При попытке удаления запросом

Код: Выделить всё

DELETE FROM Tablica WHERE ID =: OLD_ID OR PARENTI D=: OLD_ID  
он кричит "попытка удаления множественных записей!" Хотя какое ему дело? Да и если бы удалил, то удалилась бы улица и дома на этой улицы. А квартиры этих домов остались бы без родителя.

Я обдумывал вариант - дополнительное поле с какой-нибудь единой характеристикой для всех "родственников узла". Ну, скажем, RELATIONID (Integer) - при вводе нового чилда всегда равен ID самого верхнего родителя. И удалять весь пучок WHERE RELATIONID = : OLD_ID. Но а если надо будет удалять не улицу, а дом? Ведь прихватит с собой и улицу. Усложнять запрос не хочется.

Может быть, это можно сделать процедурой на самом сервере? Мой опыт пока не позволяет это оформить :-( Помогите, а?

Да, забыл сказать: в таблице есть еще поле, обозначающее уровень элемента: 0 - улица, 1 - дом, 2 - квартира, 3 - комната.

Лысый
Сообщения: 177
Зарегистрирован: 08 ноя 2004, 08:20

Re: Древовидная таблица: удаление узла с подузлами

Сообщение Лысый » 16 май 2005, 09:05

solo писал(а):Может быть, это можно сделать процедурой на самом сервере? Мой опыт пока не позволяет это оформить :-( Помогите, а?
Вот тебе ХП (утро, понедельник... если есть ошибки я не виноват :) ):

Код: Выделить всё

CREATE PROCEDURE TREE_DEL (
    ID INTEGER,
    ID_PARENT INTEGER)
AS
BEGIN
  IF (:ID <> 0 AND :ID_PARENT = 0) THEN BEGIN
      DELETE FROM TREE
       WHERE (ID = :ID);
  
      EXECUTE PROCEDURE TREE_DEL(0, :ID);
    END
  ELSE BEGIN

    FOR SELECT ID
          FROM TREE
         WHERE ID_PARENT = :ID_PARENT
          INTO :ID
    DO BEGIN
      DELETE FROM TREE
       WHERE (ID = :ID);

      EXECUTE PROCEDURE TREE_DEL(0, :ID);
    END

  END

END
Вызов:

Код: Выделить всё

EXECUTE PROCEDURE TREE_DEL(:ID, 0);

Solo
Сообщения: 108
Зарегистрирован: 18 апр 2005, 04:05

Re: Древовидная таблица: удаление узла с подузлами

Сообщение Solo » 16 май 2005, 09:22

Лысый писал(а):
solo писал(а):Может быть, это можно сделать процедурой на самом сервере? Мой опыт пока не позволяет это оформить :-( Помогите, а?
Вот тебе ХП (утро, понедельник... если есть ошибки я не виноват :) ):
Спасибо, сегодня же попробую.

break
Сообщения: 58
Зарегистрирован: 12 май 2005, 11:03

Сообщение break » 16 май 2005, 12:14

Можно сделать вторичный ключ по той же таблице (Tree)
Tree_ParentID на Tree_ID, а в On Delete указать - cascade

У меня так все работает OK!

Solo
Сообщения: 108
Зарегистрирован: 18 апр 2005, 04:05

Сообщение Solo » 17 май 2005, 03:34

break писал(а):Можно сделать вторичный ключ по той же таблице (Tree)
Tree_ParentID на Tree_ID, а в On Delete указать - cascade

У меня так все работает OK!
Гм... Не понял - мастер - деталь самой к себе с каскадным удалением? Если можно, поподробнее или ссылку на это дело.

У меня таблица Tree такая:

MID - Integer (PK) - уникальный номер и ID родителя
PARENTID - Integer - ссылка на родителя
TREE_LVL - SmallInt - уровень элемента в дереве (от 0 до 3)
Ну а дальше - поля с информацией - Наименование, адреса, деньги, даты и т.п.

И по первому предложению: я так понял, это что-то вроде рекурсивного вызова в Delphi? И тут такое же бывает?

Лысый
Сообщения: 177
Зарегистрирован: 08 ноя 2004, 08:20

Сообщение Лысый » 17 май 2005, 08:40

Гм... Не понял - мастер - деталь самой к себе с каскадным удалением? Если можно, поподробнее или ссылку на это дело.

Код: Выделить всё

CREATE TABLE TREE (
    ID         INTEGER NOT NULL,
    ID_PARENT  INTEGER,
    CNT        INTEGER
);

ALTER TABLE TREE ADD CONSTRAINT PK_TREE PRIMARY KEY (ID);

ALTER TABLE TREE ADD CONSTRAINT FK_TREE FOREIGN KEY (ID_PARENT) REFERENCES TREE (ID) ON DELETE CASCADE;

solo писал(а):И по первому предложению: я так понял, это что-то вроде рекурсивного вызова в Delphi? И тут такое же бывает?
Бывает :) чем мы хуже?

Только имей ввиду, что оба варианта надо использовать с осторожностью. Каскадное удаление вещь опасная и требует осмысленного применения.

Ivan_Pisarevsky
Заслуженный разработчик
Сообщения: 644
Зарегистрирован: 15 фев 2005, 11:34

Re: Древовидная таблица: удаление узла с подузлами

Сообщение Ivan_Pisarevsky » 17 май 2005, 10:12

Лысый писал(а): Вот тебе ХП (утро, понедельник... если есть ошибки я не виноват :) ):

Код: Выделить всё

CREATE PROCEDURE TREE_DEL (
    ID INTEGER,
    ID_PARENT INTEGER)
AS
BEGIN
  IF (:ID <> 0 AND :ID_PARENT = 0) THEN BEGIN
      DELETE FROM TREE
       WHERE (ID = :ID);
  
      EXECUTE PROCEDURE TREE_DEL(0, :ID);
    END
  ELSE BEGIN

    FOR SELECT ID
          FROM TREE
         WHERE ID_PARENT = :ID_PARENT
          INTO :ID
    DO BEGIN
      DELETE FROM TREE
       WHERE (ID = :ID);

      EXECUTE PROCEDURE TREE_DEL(0, :ID);
    END

  END

END
Вызов:

Код: Выделить всё

EXECUTE PROCEDURE TREE_DEL(:ID, 0);
Мизерное уточнение, чтоб рекурсивную процедуру скормить серверу, надо создать оную сначала пустую :wink:

Код: Выделить всё

CREATE PROCEDURE TREE_DEL (
    ID INTEGER,
    ID_PARENT INTEGER)
AS
BEGIN
  EXIT;
END

ALTER PROCEDURE TREE_DEL (
    ID INTEGER,
    ID_PARENT INTEGER)
AS
BEGIN

  IF (:ID <> 0 AND :ID_PARENT = 0) THEN BEGIN
      DELETE FROM TREE
       WHERE (ID = :ID);
  
      EXECUTE PROCEDURE TREE_DEL(0, :ID);
    END
  ELSE BEGIN

    FOR SELECT ID
          FROM TREE
         WHERE ID_PARENT = :ID_PARENT
          INTO :ID
    DO BEGIN
      DELETE FROM TREE
       WHERE (ID = :ID);

      EXECUTE PROCEDURE TREE_DEL(0, :ID);
    END

  END

END
PS. корректность собственно процедуры я не проверял.

Solo
Сообщения: 108
Зарегистрирован: 18 апр 2005, 04:05

Сообщение Solo » 19 май 2005, 05:13

Спасибо Break-y, опробовал его вариант с мастер-деталь деревянной таблицы самой к себе. Сработало!!! Правда, задать форин-индекс в IB Experte на рабочей базе почему-то не вышло, вылазил какой-то AV по первичному индексу :(

Я перегнал голые мета-данные в скрипт, добавил там строку Break-a и выполнил его. Все вышло!!!

Интересно, а когда база набьется большим количеством данных, проблем не будет? Сейчас-то там от силы 50 записей. И в чем надо быть осторожным?

С вариантом с процедурой так и не добил пока...

Лысый
Сообщения: 177
Зарегистрирован: 08 ноя 2004, 08:20

Сообщение Лысый » 19 май 2005, 08:52

solo писал(а):Спасибо Break-y, опробовал его вариант с мастер-деталь деревянной таблицы самой к себе. Сработало!!! Правда, задать форин-индекс в IB Experte на рабочей базе почему-то не вышло, вылазил какой-то AV по первичному индексу :(
AV - это access violation? Если да то изменения в методанные надо вносить в монопольном режиме.
solo писал(а): Интересно, а когда база набьется большим количеством данных, проблем не будет?
Если у тебя 4 уровня вложенности, то какие ожидаешь проблемы?
solo писал(а): И в чем надо быть осторожным?
Если ты все листья породишь из одного корня и грохнешь корневой узел, то потеряешь все данные. Понятно?
solo писал(а):С вариантом с процедурой так и не добил пока...
А и не надо, если не требуется дополнительный анализ.

Solo
Сообщения: 108
Зарегистрирован: 18 апр 2005, 04:05

Сообщение Solo » 19 май 2005, 09:57

Лысый писал(а): AV - это access violation? Если да то изменения в методанные надо вносить в монопольном режиме.
Он самый. Точно, я был не в монопольном.
solo писал(а): И в чем надо быть осторожным? Если ты все листья породишь из одного корня и грохнешь корневой узел, то потеряешь все данные. Понятно?
Хм... Ну это-то понятно :lol: , ради этого дерево и есть ... У меня в базе нет единого корневого узла. Я думал, речь идет о каких-нибудь глюках по сети или еще что-нибудь. Ну, вроде вопрос исчерпан. Всем спасибо

Ответить