In this tutorial, we will learn what is hierarchical data, what is adjacency list mode and how to manage hierarchical data in MySQL using the adjacency list model. So, let’s get started!
Introduction to Adjacency List Model and Hierarchical Data
Hierarchical data means the data is organized in a tree fashion, i.e., in a hierarchical manner. For example, vehicle->car->honda is a hierarchy of data. Hierarchical data is also known as a tree and every individual data is known as a node.
To manage the hierarchical data, there are many ways; however, an adjacency list model is a best and simplest way.
In the adjacency list model, each node is assigned a pointer that points to its parent.
Let’s understand the tree terminologies first before performing operations on hierarchical data/tree data.
Tree Data Terminologies
- A parent node is a node which derives the new node.
- A child node is a node which is derived from another node.
- The top node which doesn’t have any parent is the root node
- Nodes which don’t have any children are leaf nodes.
- The path is the way from the given node to the specific node.
- Ancestors are a set of all parents, grandparents, etc.
- Descendants are a set of all children, grandchildren etc.
Check the below image to understand the terminologies.
Here, Animal is a root node because it doesn’t have any parent node.
A Reptile and mammal are children of the animal whereas the animal is the parent of the reptile and mammal.
The path from animal to salamander is – Animal -> Reptile -> Lizard -> Salamander
Managing Hierarchical Data Examples
To manage the hierarchical data in MySQL, first, we need to create a table and insert the data of the above tree structure.
CREATE TABLE animals(
id INT AUTO_INCREMENT PRIMARY KEY,
animal_name VARCHAR(20) NOT NULL,
parent_id INT,
is_active INT DEFAULT 1,
FOREIGN KEY(parent_id) REFERENCES animals(id)
ON DELETE CASCADE ON UPDATE CASCADE
);
Code language: SQL (Structured Query Language) (sql)
Here, we have created an id column to store the id of each node. The second column will consist names of nodes. The third column will consist of the parent_id which is the id of a parent node. The Forth column will consist of either 0 or 1 where 1 means the node is active and 0 means the node is inactive.
We referenced the foreign key to the same table as the same table will contain the child id and parent id. Because, whenever we change the parent_id, changes must be made in its children also.
Let’s insert the data as shown in the image above.
INSERT INTO animals(animal_name,parent_id)VALUES
("animal",NULL),("Reptile",1),("Lizard",2),("Salamander",3),
("Snake",2),("Crocodile",2),("Mammal",1),("Equine",7),
("Horse",8),("Zebra",8),("Bovine",7),("Cow",11),
("Canine",7),("Wolf",13),("Fox",13);
Code language: SQL (Structured Query Language) (sql)
SELECT * FROM animals;
Code language: SQL (Structured Query Language) (sql)
Let’s begin to manage the hierarchical data one by one.
Finding The Root Node
SELECT * FROM animals WHERE parent_id IS NULL;
Code language: SQL (Structured Query Language) (sql)
Finding Immediate Children of any node
SELECT * FROM animals WHERE parent_id = 1
Code language: SQL (Structured Query Language) (sql)
Finding All Leaf Nodes
SELECT a1.* FROM animals a1
LEFT JOIN animals a2
ON a1.id=a2.parent_id
WHERE a2.id IS NULL;
Code language: SQL (Structured Query Language) (sql)
Displaying Path to Every Node from Root Node
Here, we will use a recursive CTE to fetch the complete tree structure.
WITH RECURSIVE animal_cat AS
(SELECT id, animal_name,parent_id,
1 AS depth, animal_name AS path FROM animals
WHERE id = 1
UNION ALL
SELECT a.id,a.animal_name,a.parent_id,ac.depth+1,
CONCAT(ac.path,' --> ',a.animal_name) FROM animal_cat AS ac
JOIN animals AS a ON a.parent_id=ac.id
)
SELECT * FROM animal_cat;
Code language: SQL (Structured Query Language) (sql)
Display Subtree of Given Node
Here, we will find the subtree of a given node, i.e, all descendants of a given node.
We will find the subtree of a mammal node. The id of a mammal node is 7.
WITH RECURSIVE animal_cat AS
(SELECT id, animal_name,parent_id,
1 AS depth, animal_name AS path FROM animals
WHERE id = 7
UNION ALL
SELECT a.id,a.animal_name,a.parent_id,ac.depth+1,
CONCAT(ac.path,' --> ',a.animal_name) FROM animal_cat AS ac
JOIN animals AS a ON a.parent_id=ac.id
)
SELECT * FROM animal_cat;
Code language: SQL (Structured Query Language) (sql)
Skipping Inactive Nodes and Descendants
Here, we will display only active nodes, i.e., those nodes which have a value of is_active attribute as 1.
By default, we have set each node as active. Let’s make a node having id 2 inactive. The node having id 2 is a reptile.
So, the whole subtree of a node reptile will become inactive.
UPDATE animals SET is_active=0 WHERE id=2;
Code language: SQL (Structured Query Language) (sql)
WITH RECURSIVE animal_cat AS
(SELECT id, animal_name,parent_id,
1 AS depth, animal_name AS path FROM animals
WHERE id = 1
UNION ALL
SELECT a.id,a.animal_name,a.parent_id,ac.depth+1,
CONCAT(ac.path,' --> ',a.animal_name) FROM animal_cat AS ac
JOIN animals AS a ON a.parent_id=ac.id
WHERE a.is_active=1
)
SELECT * FROM animal_cat;
Code language: SQL (Structured Query Language) (sql)
Calculating the Level of Each Node
You can think of a Level of a node as a floor of a tree but the count starts from the top node and it’s 0 initially.
For example, the level of the ‘animal’ node is 0, ‘mammal’ and ‘reptile’ is 1, and so on.
WITH RECURSIVE animal_cat AS
(SELECT id, animal_name, 0 AS level FROM animals
WHERE id = 1
UNION ALL
SELECT a.id,a.animal_name,ac.level + 1
FROM animal_cat AS ac
JOIN animals AS a ON a.parent_id=ac.id
)
SELECT * FROM animal_cat ORDER BY level;
Code language: SQL (Structured Query Language) (sql)
Moving Subtree to New Parent
You can move a whole subtree to the new destination by updating the parent id of the node.
Here, we will move the subtree of a node ‘Equine’ to the ‘Bovine’. So, we have to change the parent_id of the ‘Equine’ node.
UPDATE animals SET parent_id = 11 WHERE id=8
Code language: SQL (Structured Query Language) (sql)
Deleting a Node and Its Descendants
When we delete any node, all descendants will be deleted automatically as we have applied the ‘ON DELETE’ cascade on the foreign key.
Here, we will delete the node Reptile which will delete all its nodes- lizard, snake, crocodile and salamander.
DELETE FROM animals WHERE id=2;
Code language: SQL (Structured Query Language) (sql)
The result of updating and deleting nodes is –
WITH RECURSIVE animal_cat AS
(SELECT id, animal_name,parent_id,
1 AS depth, animal_name AS path FROM animals
WHERE id = 1
UNION ALL
SELECT a.id,a.animal_name,a.parent_id,ac.depth+1,
CONCAT(ac.path,' --> ',a.animal_name) FROM animal_cat AS ac
JOIN animals AS a ON a.parent_id=ac.id
)
SELECT * FROM animal_cat;
Code language: SQL (Structured Query Language) (sql)
Summary
In this tutorial, we have learned-
- What the Hierarchical Data and Adjacency List model is
- Tree data terminologies
- How to find a root node
- How to find children of a node
- How to find leaf nodes
- How to display the whole tree
- Display subtree
- How to Calculate the level of each node
- Update and delete nodes