Implementacja metody 4

Schemat bazy danych jest taki jak w artykule o metodzie IV, zatem możemy pominąc opis i prześć do implementacji. W moim rozwiązaniu założyłem, że wszelkie operacje modyfikacji jakie będę wykonywał na strukturze bądą dotyczyły tylko tabeli tree. Tabela tree_pos będzie modyfikowana za pomocą wyzwalaczy. Dodam jeszcze, iż kod przedstawiony działa na bazie danych PostgreSQL (na słoniku). W przypadku innych systemów bazodanowych, tę obsługę trzeba będzie lekko zmodyfikowaćlub przeniaść ją do aplikacji. Oczywiście, wiem, że umieszczenie tych procedur w wyzwalaczach niesie za sobą spowolnienie bazy danych, dlatego jeżeli ktoś ma zamiar tworzyć takie drzewko dla większej ilości danych, to niech lepiej nie robi tego na wyzwalaczach. Nie polecam.

Penwie się ktośzastanawiał czy chciało mi się tworzyć pokolei rekordy w tabeli tree na potrzeby poprzedniego artykułu. Otóż nie, napisałem sobie wcześniej taki prosty wyzwalacz który zrobił to za mnie.

CREATE OR REPLACE FUNCTION tree_a_i () RETURNS TRIGGER AS '
DECLARE
BEGIN
--wstawiamy pierwszy rekord z poziomem glebokosci =0
INSERT INTO tree_pos (tree_id, parent_id, depth) VALUES (NEW.id, NEW.id, 0);
--jeżeli nie jest to pierwszy element w całym drzewku (główny korzeń)
IF NEW.tree_id IS NOT NULL THEN
--utwóż powiązania z rodzicami na podstawie powiazań rodzica
INSERT INTO tree_pos (tree_id, parent_id, depth)
SELECT NEW.tree_id, parent_id, depth + 1 FROM tree_pos WHERE tree_id = NEW.tree_id;
END IF;
RETURN NEW;
END;
' LANGUAGE 'plpgsql';

CREATE TRIGGER trg_tree_a_i AFTER INSERT ON tree FOR EACH ROW EXECUTE PROCEDURE tree_a_i ();

Zatem wstawianie elementów do drzewka mamy opracowane.

Życie stawia nas często w różnych sytuacjach. Jedną z takich jest zmiana rodzica. W prawdzie nie poczułem tego na własnej skórze, to i tak nikomu tego nie życzę. I tak jak w życiu tak czasem bywa również w programowaniu - zmiana rodzica czyli przenoszenie.

Sprawa jest dosyć prosta jeżeli przenoszony element nie ma żadnych "dzieci". Wystarczy tylko usunąc wszystko z tabeli tree_pos i wstawić jeszcze raz z użyciem nowego PARENT_ID:

DELETE FROM tree_pos WHERE tree_id=NASZE_TREE_ID AND depth>0;
UPDATE tree SET parent_id=NASZ_NOWY_PARENT_ID WHERE tree_id=NASZE_TREE_ID;
INSERT INTO tree_pos (tree_id, parent_id, depth)
SELECT NASZE_TREE_ID, parent_id, depth + 1 FROM tree_pos WHERE tree_id = NASZ_NOWY_PARENT_ID;

Schody się pojawiają kiedy chcemy zmienić rodzica "całej rodzinie". Chcąc wykorzystać powyższą metodę powinniśmy usunąc wszystkie rekordy z tree_pos dla całej przenoszonej rodziny. Teraz mogę śmiało powiedzieć po co tak na prawdę był mi potrzebny parent_id w tabeli tree. W prawdzie można usunąć z tree_pos wszystkie rekordy, których depth>1. Jednakże chcąc oprogramować w dość prosty sposób procedurę przenoszenia, szybko odczułem brak tego pola.

CREATE OR REPLACE FUNCTION trg_tree_chparent_a_u () RETURNS TRIGGER AS '
DECLARE
v_kid integer;
v_pid integer;
v_row RECORD;
BEGIN
IF NEW.parent_id<>OLD.parent_id THEN
IF (SELECT count(*) FROM kat_pos WHERE NEW.kat_id=parent_id AND NEW.parent_id=kat_id AND stan=1)>0 THEN
RETRUN OLD;
END IF;
UPDATE kat_pos SET stan=0
WHERE kat_id=NEW.kat_id AND stan=1 AND depth>0;
INSERT INTO kat_pos (kat_id,parent_id,depth)
SELECT NEW.kat_id, parent_id, depth+1
FROM kat_pos
WHERE kat_id=NEW.parent_id AND stan=1;
FOR v_row IN (SELECT kat_id FROM kat_pos
WHERE parent_id=NEW.kat_id AND stan=1 AND depth>0 ORDER BY depth) LOOP
v_kid:=v_row.kat_id;
UPDATE kat_pos SET stan=0 WHERE kat_id=v_kid AND depth>0 AND stan=1;
SELECT parent_id INTO v_pid FROM kat WHERE kat_id=v_kid;
INSERT INTO kat_pos (kat_id,parent_id,depth)
SELECT v_kid, parent_id, depth+1 FROM kat_pos WHERE kat_id=v_pid AND stan=1;
END LOOP;
END IF;
RETURN NEW;
END;
' LANGUAGE 'plpgsql';

Zamieszczam dodatkowo Dump`a z bazy na której opisane procedury działają bez zarzutu.