Last active
June 7, 2022 16:12
-
-
Save defndaines/628c706795169a3fb4cd3d7f70a607d7 to your computer and use it in GitHub Desktop.
Transitive Closure of Acyclic Graphs [PostgreSQL]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Maintaining Transitive Closure of Graphs in SQL (1999) | |
-- https://corescholar.libraries.wright.edu/knoesis/399/ | |
-- Set up | |
DROP TABLE IF EXISTS graph; | |
DROP TABLE IF EXISTS transitive_closures; | |
CREATE TABLE graph ( | |
x VARCHAR(5) NOT NULL, | |
y VARCHAR(5) NOT NULL | |
); | |
CREATE UNIQUE INDEX index_graph_edges ON graph (x, y); | |
CREATE TABLE transitive_closures ( | |
x VARCHAR(5) NOT NULL, | |
y VARCHAR(5) NOT NULL | |
); | |
CREATE UNIQUE INDEX index_transitive_closure_edges ON transitive_closures (x, y); | |
-- 2 Transitive Closures of Acyclic Graphs | |
-- Set up | |
INSERT INTO graph (x, y) VALUES ('x', 'a'), ('b', 'y'); | |
INSERT INTO transitive_closures (x, y) VALUES ('x', 'a'), ('b', 'y'); | |
--- | |
-- Maintenance Under Insertion | |
-- Insertion of edge (a, b). | |
INSERT INTO graph (x, y) VALUES ('a', 'b'); | |
WITH vars (a, b) AS (VALUES ('b', '1')) | |
SELECT * | |
INTO TEMP tc_new | |
FROM (SELECT a AS x, b AS y FROM vars | |
UNION SELECT x, b AS y | |
FROM transitive_closures, vars | |
WHERE y = a | |
UNION SELECT a, y | |
FROM transitive_closures, vars | |
WHERE x = b | |
UNION SELECT tc1.x, tc2.y | |
FROM transitive_closures AS tc1, transitive_closures AS tc2, vars | |
WHERE tc1.y = a AND tc2.x = b | |
) AS _; | |
SELECT * | |
INTO TEMP delta | |
FROM tc_new AS t | |
WHERE NOT EXISTS ( | |
SELECT * | |
FROM transitive_closures | |
WHERE transitive_closures.x = t.x AND transitive_closures.y = t.y | |
); | |
INSERT INTO transitive_closures SELECT * FROM delta; | |
-- Expected outcome: | |
-- | |
-- (x) -> old path -> (a) -> new path -> (b) -> old path -> (y) | |
-- +---> new path -----------------------^ | |
-- | +---> new path -----------------------^ | |
-- +---> new path ------------------------------------------^ | |
--- | |
-- Maintenance Under Deletions | |
-- Set up | |
INSERT | |
INTO graph (x, y) | |
VALUES ('0', 'a'), | |
('0', 'c'), | |
('a', 'b'), | |
('c', 'a'), | |
('c', 'b'), | |
('b', '1'); | |
INSERT | |
INTO transitive_closures | |
VALUES ('0', 'a'), | |
('0', 'c'), | |
('a', 'b'), | |
('0', 'b'), | |
('c', 'b'), | |
('c', 'a'), | |
('c', '1'), | |
('a', '1'), | |
('0', '1'), | |
('b', '1'); | |
-- Step 1: Finding the suspects | |
WITH vars (a, b) AS (VALUES ('a', 'b')) | |
SELECT * | |
INTO TEMP suspect | |
FROM (SELECT tc1.x, tc2.y | |
FROM transitive_closures AS tc1, transitive_closures AS tc2, vars | |
WHERE tc1.y = a AND tc2.x = b | |
UNION SELECT x, b | |
FROM transitive_closures, vars | |
WHERE y = a | |
UNION SELECT a, y | |
FROM transitive_closures, vars | |
WHERE x = b | |
UNION SELECT a, b FROM vars | |
) AS _; | |
-- Step 2: Finding the trusty guys | |
WITH vars (a, b) AS (VALUES ('a', 'b')) | |
SELECT * | |
INTO TEMP trusty | |
FROM ( | |
SELECT * FROM transitive_closures WHERE NOT EXISTS ( | |
SELECT * | |
FROM suspect | |
WHERE suspect.x = transitive_closures.x AND suspect.y = transitive_closures.y | |
) | |
UNION SELECT graph.* | |
FROM graph, vars | |
WHERE x <> a AND y <> b | |
) AS _; | |
-- Step 3: Deleting the bad guys | |
SELECT * | |
INTO TEMP tc_new | |
FROM (SELECT * FROM trusty | |
UNION SELECT t1.x, t2.y | |
FROM trusty t1, trusty t2 | |
WHERE t1.y = t2.x | |
UNION SELECT t1.x, t3.y | |
FROM trusty t1, trusty t2, trusty t3 | |
WHERE t1.y = t2.x AND t2.y = t3.x | |
) AS _; | |
DELETE FROM transitive_closures | |
WHERE NOT EXISTS( | |
SELECT * | |
FROM tc_new AS t | |
WHERE t.x = transitive_closures.x AND t.y = transitive_closures.y | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
And here are the triggers to make this behavior automatic on
INSERT
,DELETE
, andTRUNCATE
.