Here's how I would typically model a hierarchy with a closure table:
CREATE TABLE nodes
( node_id int not null primary key
, node_name int not null
, -- other attributes of a node
);
CREATE TABLE parent_nodes
( node_id int not null primary key
references node (node_id)
, parent_id int not null
references nodes (node_id)
);
CREATE TABLE ancestor_nodes
( node_id int not null
references nodes (node_id)
, ancestor_id not null
references nodes (node_id)
);
ancestor_nodes can be maintained by triggers. Adding a node x to parent y implies that x cross join (ancestors of y union y) should be added to ancestor_nodes. Deleting a node is also trivial, and moving a node is more or less the same as adding and deleting a node. Example of an insert trigger for maintaining transitive closure of parent_nodes:
create trigger add_closure
after insert on parent_nodes
referencing new as n
for each row
begin
insert into ancestor_nodes (node_id, ancestor_id)
values (n.node_id, n.parent_id)
union all
select n.node_id, ancestor_id
from ancestor_nodes
where node_id = n.parent_id
union all
select node_id, n.parent_id
from ancestor_nodes
where ancestor_id = n.node_id
union all
select a.node_id, b.ancestor_id
from ancestor_nodes a
cross join ancestor_nodes b
where a.ancestor_id = n.node_id
and b.node_id = n.parent_id; --
end;
The exact syntax will vary between vendors, but it should be easy to port to other ones. The above syntax is for Db2, and I added a Fiddle
If you want to express the rule "two nodes with the same parent cannot have the same name", you could either use a validation trigger for that or merge nodes and parent_nodes into one relation, say Nodes.
One of the problems with merging this into one relation is how to represent the parent of a root_node? There are basically two options:
a) parent(x) := x
b) parent(x) := null
If your DBMS does not allow you to use NULL in a UNIQUE constraint, it leaves you with option a) You could then add a unique constraint like:
UNIQUE (parent_id, node_name)
EDIT: A third option is to denormalize name so that it becomes an attribute of parent. I've updated a small example of this at: Fiddle2
Node
allowed to have more than 1 "parent"?