Skip to content

Instantly share code, notes, and snippets.

@leonlee
Forked from kentoj/closure-table-operations.sql
Created December 31, 2021 07:12
Show Gist options
  • Save leonlee/b43c976a7a62cb9ed9f165621ca02516 to your computer and use it in GitHub Desktop.
Save leonlee/b43c976a7a62cb9ed9f165621ca02516 to your computer and use it in GitHub Desktop.
Closure Table operations SQL fragments
-- Retrieve descendants
-- ====================
-- retrieve descendants of #4
SELECT c.*
FROM Comments AS c
JOIN TreePaths AS t ON c.comment_id = t.descendant
WHERE t.ancestor = 4;
-- Retrieve ancestors
-- ==================
-- retrieve ancestors of #6
SELECT c.*
FROM Comments AS c
JOIN TreePaths AS t ON c.comment_id = t.ancestor
WHERE t.descendant = 6;
-- Insert Leaf node
-- ================
-- insert leaf node #8 as a child of #5
INSERT INTO TreePaths (ancestor, descendant, path_length)
SELECT t.ancestor, 8, t.path_length + 1
FROM TreePaths AS t
WHERE t.descendant = 5
UNION ALL
SELECT 8, 8;
-- Delete Leaf node
-- ================
-- delete leaf node #7
DELETE FROM TreePaths WHERE descendant = 7;
-- Delete Subtree
-- ==============
-- delete #4 and all children from the tree
DELETE FROM TreePaths
WHERE descendant IN (SELECT descendant
FROM TreePaths
WHERE ancestor = 4);
-- Move Subtree (2 steps)
-- ============
-- reparent #6 from #4 -> #3
--
-- Step 1: Disconnect from current ancestors
-- -----------------------------------------
-- delete all paths that end at descendants in the current node's subtree
-- and that begin at ancestors of the current node (6).
DELETE FROM TreePaths
WHERE descendant IN (SELECT descendant
FROM TreePaths
WHERE ancestor = 6)
AND ancestor IN (SELECT ancestor
FROM TreePaths
WHERE descendant = 6
AND ancestor != descendant);
-- Step 2: Insert rows matching ancestors of insertion point and descendants of subtree
-- ------------------------------------------------------------------------------------
-- This uses CROSS JOIN to get the cross product of the new parent's ancestors, including the new parent,
-- with the subtree's nodes. This is one case where the full cartesian product is useful.
INSERT INTO TreePaths (ancestor, descendant, path_length)
SELECT
supertree.ancestor,
subtree.descendant,
supertree.path_length + subtree.path_length + 1 AS path_length
FROM TreePaths AS supertree
CROSS JOIN TreePaths AS subtree
WHERE supertree.descendant = 3
AND subtree.ancestor = 6;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment