9

Mam DAG w mojej relacyjnej bazie danych (Firebird) z dwiema tabelami edge i node (model listy sąsiedztwa). Chcę zapytać je rekursywnie, ale znaleziono rekursywne zapytania bardzo nieefektywne. Tak więc próbowałem wprowadzić wyzwalacze, aby utrzymać zamknięcie przechodnie po Dong et.al. papier http://homepages.inf.ed.ac.uk/libkin/papers/tc-sql.pdf.W jaki sposób sprawnie zarządzać przechodnią?

s są teraz bardzo szybkie, ale DELETE s są bardzo powolne, ponieważ prawie cały wykres jest kopiowany dla pojedynczego usunięcia. Co gorsza, współbieżne aktualizacje wydają się niemożliwe.

Czy istnieje lepszy sposób realizacji tego?

Edit

zrobiłem kilka eksperymentów i wprowadzono licznik odniesienia do tabeli TC. Dzięki temu usuwanie jest szybkie. Napisałem kilka prostych przypadków testowych, ale nie jestem pewien, czy dobrze mi idzie. Oto, co mam do tej pory:

CREATE GENERATOR graph_tc_seq; 

CREATE TABLE EDGE (
    parent DECIMAL(10, 0) NOT NULL, 
    child DECIMAL(10, 0) NOT NULL, 
    PRIMARY KEY (parent, child) 
); 

CREATE TABLE GRAPH_TC (
    parent DECIMAL(10, 0) NOT NULL, 
    child DECIMAL(10, 0) NOT NULL, 
    refcount DECIMAL(9, 0), 
    PRIMARY KEY (parent, child) 
); 

CREATE TABLE GRAPH_TC_TEMP (
    session_id DECIMAL(9, 0), 
    parent DECIMAL(10, 0), 
    child DECIMAL(10, 0) 
); 

CREATE PROCEDURE GRAPH_TC_CREATE (p_parent DECIMAL(10, 0), c_child DECIMAL(10, 0)) 
AS 
    declare variable tp_parent DECIMAL(10,0); 
    declare variable tc_child DECIMAL(10,0); 
    declare variable session_id DECIMAL(9,0); 
    declare variable refs DECIMAL(9,0); 
begin 
    session_id = gen_id(graph_tc_seq,1); 
    insert into graph_tc_temp (parent, child, session_id, refcount) values (:p_parent, :p_parent, :session_id, 1); 
    insert into graph_tc_temp (parent, child, session_id, refcount) values (:c_child, :c_child, :session_id, 1); 
    insert into graph_tc_temp (parent, child, session_id, refcount) values (:p_parent, :c_child, :session_id, 1); 
    insert into graph_tc_temp (parent, child, session_id, refcount) select distinct :p_parent, child, :session_id, refcount from graph_tc where parent = :c_child and not parent = child; 
    insert into graph_tc_temp (child, parent, session_id, refcount) select distinct :c_child, parent, :session_id, refcount from graph_tc where child = :p_parent and not parent = child; 
    insert into graph_tc_temp (parent, child, session_id, refcount) select distinct a.parent, b.child, :session_id, a.refcount*b.refcount from graph_tc a, graph_tc b where a.child = :p_parent and b.parent = :c_child and not a.parent = a.child and not b.parent = b.child; 
    for select parent, child, refcount from graph_tc_temp e where session_id= :session_id and exists (select * from graph_tc t where t.parent = e.parent and t.child = e.child) into :tp_parent, :tc_child, :refs do begin 
     update graph_tc set refcount=refcount+ :refs where parent = :tp_parent and child = :tc_child; 
    end 
    insert into graph_tc (parent, child, refcount) select parent, child, refcount from graph_tc_temp e where session_id = :session_id and not exists (select * from graph_tc t where t.parent = e.parent and t.child = e.child); 
    delete from graph_tc_temp where session_id = :session_id; 
end^

CREATE PROCEDURE GRAPH_TC_DELETE (p_parent DECIMAL(10, 0), c_child DECIMAL(10, 0)) 
AS 
    declare variable tp_parent DECIMAL(10,0); 
    declare variable tc_child DECIMAL(10,0); 
    declare variable refs DECIMAL(9,0); 
begin 
    delete from graph_tc where parent = :p_parent and child = :p_parent and refcount <= 1; 
    update graph_tc set refcount = refcount - 1 where parent = :p_parent and child = :p_parent and refcount > 1; 
    delete from graph_tc where parent = :c_child and child = :c_child and refcount <= 1; 
    update graph_tc set refcount = refcount - 1 where parent = :c_child and child = :c_child and refcount > 1; 
    delete from graph_tc where parent = :p_parent and child = :c_child and refcount <= 1; 
    update graph_tc set refcount = refcount - 1 where parent = :p_parent and child = :c_child and refcount > 1; 
    for select distinct :p_parent, b.child, refcount from graph_tc b where b.parent = :c_child and not b.parent = b.child into :tp_parent, :tc_child, :refs do begin 
     delete from graph_tc where parent = :tp_parent and child = :tc_child and refcount <= :refs; 
     update graph_tc set refcount = refcount - :refs where parent = :tp_parent and child = :tc_child and refcount > :refs; 
    end 
    for select distinct :c_child, b.parent, refcount from graph_tc b where b.child = :p_parent and not b.parent = b.child into :tc_child, :tp_parent, :refs do begin 
     delete from graph_tc where child = :tc_child and parent = :tp_parent and refcount <= :refs; 
     update graph_tc set refcount = refcount - :refs where child = :tc_child and parent = :tp_parent and refcount > :refs; 
    end 
    for select distinct a.parent, b.child, a.refcount*b.refcount from graph_tc a, graph_tc b where not a.parent = a.child and not b.parent = b.child and a.child = :p_parent and b.parent = :c_child into :tp_parent, :tc_child, :refs do begin 
     delete from graph_tc where parent = :tp_parent and child = :tc_child and refcount <= :refs; 
     update graph_tc set refcount = refcount - :refs where parent = :tp_parent and child = :tc_child and refcount > :refs; 
    end 
end^

CREATE TRIGGER GRAPH_TC_AFTER_INSERT FOR EDGE AFTER INSERT as 
begin 
    execute procedure graph_tc_create(new.parent,new.child); 
end^

CREATE TRIGGER GRAPH_TC_AFTER_UPDATE FOR EDGE AFTER UPDATE as 
begin 
    if ((new.parent <> old.parent) or (new.child <> old.child)) then begin 
    execute procedure graph_tc_delete(old.parent,old.child); 
    execute procedure graph_tc_create(new.parent,new.child); 
    end 
end^

CREATE TRIGGER GRAPH_TC_AFTER_DELETE FOR EDGE AFTER DELETE as 
begin 
    execute procedure graph_tc_delete(old.parent,old.child); 
end^

To jest mój własny pomysł, ale myślę, że inni już wdrożyli TC. Czy robią to samo?

Mam kilka przypadków testowych, ale nie jestem pewien, czy mogę uzyskać niespójność z większymi wykresami.

Co powiesz na współbieżność? Myślę, że takie podejście zakończy się niepowodzeniem, gdy dwie równoczesne transakcje będą wymagać aktualizacji wykresu, prawda?

Edit

znalazłem kilka błędów w kodzie, i chciałbym podzielić się z wami w wersji stałej.

Znalazłem świetny artykuł: http://www.codeproject.com/Articles/22824/A-Model-to-Represent-Directed-Acyclic-Graphs-DAG-o. Czy istnieje więcej interesujących artykułów lub artykułów naukowych o różnych podejściach?

+0

Czy można uwzględnić (odpowiednie części) definicję DDL i wyzwalacza? –

+0

@MarkRotteveel zobacz moją edycję –

+2

Rozważ użycie [GTT (globalna tabela tymczasowa)] (http://www.firebirdsql.org/file/documentation/reference_manuals/reference_material/html/langrefupd25-ddl-table.html) zamiast normalna tabela dla 'GRAPH_TC_TEMP' –

Odpowiedz

1

Po prostu naprawiłem powolną operację usuwania, rozszerzając ją do modelu przechyłowej refleksyjnej tabeli zamknięć opisanej tutaj: http://www.dba-oracle.com/t_sql_patterns_incremental_eval.htm. Trzeba było trochę więcej pracy, aby w pełni utrzymać ścieżki w nim liczone, ale to się opłacało, gdy usuwanie zostało przerwane z 6 sekund każdej pojedynczej operacji usunięcia do pomijalnej (mogę teraz usunąć wszystkie relacje na wykresie, a następnie dodać je wszystkie z powrotem w 14 sekund w sumie dla 4000 relacji).

+0

A dla bonusu, najkrótsze ścieżki mogą być utrzymywane podobnie jak całkowita liczba ścieżek http://www.tjhsst.edu/~latmate/acm/DatabaseSystems/ShortestDistanceinFirstOrderLogicSQLp698-pangTODS-Oct05.pdf – nclu

4

SQL nie jest właściwym narzędziem do obsługi wykresów. Użyj jednego z nich:

http://en.wikipedia.org/wiki/Graph_database

bardzo lubię ArangoDB, wich mają syntaxe blisko MongoDB.

+0

Mam świadomość, że wykres db byłby idealnym rozwiązaniem; ale dla dwóch wykresów o krawędzi <100k każdy nie dodaję nowej bazy danych. –

0

Spróbuj utworzyć indeksy dla odpowiednich klauzul , gdzie (np .: child, parent).

Nie znam Firebirda, ale zobacz, jak działa "opis firebirda" i sprawdź, czy używa właściwego indeksowania, aby przyspieszyć selekcję w swoich procedurach.

Nawet tworzenie indeksów utraconych w wydajności dla aktualizowania/usuwania/wstawiania, w twoim przypadku może poprawić wynik.

+0

Rzeczywiste wdrożenie ma indeksy; Po prostu nie skopiowałem instrukcji "CREATE INDEX" do powyższego kodu. –