PostgreSQL: Saving categories and subcategories in one table

Updated: January 25, 2024 By: Guest Contributor Post a comment

Introduction

Organizing categories and subcategories effectively in a relational database can be a challenging yet crucial part of database design, especially when maintaining the hierarchical relationship in a single table. This tutorial will guide you through the process of saving categories and subcategories in a single PostgreSQL table using common table expressions for efficient querying.

While there are several ways to structure hierarchical data in a database, we will focus on the ‘adjacency list model’ due to its simplicity and ease of understanding. An adjacency list essentially keeps a reference to the parent category within the same table.

Database Table Design

To start, we will create a simple table that will hold our categories and subcategories.

CREATE TABLE categories (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    parent_id INT REFERENCES categories(id) ON DELETE CASCADE
);

The table includes an ID for each category, a ‘name’ column, and a ‘parent_id’ column which refers to the ‘id’ of its parent category. PostgreSQL allows a foreign key to reference the same table which is essential for maintaining this relationship.

Inserting Category Data

INSERT INTO categories (name, parent_id) VALUES
   ('Electronics', NULL),
   ('Laptops', 1),
   ('Tablets', 1),
   ('Gaming Laptops', 2);

When inserting records, the parent category does not have a ‘parent_id’ (NULL), while each subcategory has the ‘parent_id’ corresponding to its parent category.

Querying the Categories

To query the hierarchy, we will utilize a ‘WITH RECURSIVE’ query.

WITH RECURSIVE subcategories AS (
    SELECT id, name, parent_id
    FROM categories
    WHERE parent_id IS NULL
    UNION ALL
    SELECT c.id, c.name, c.parent_id
    FROM categories c
    INNER JOIN subcategories s ON s.id = c.parent_id
)
SELECT * FROM subcategories;

This recursive CTE (common table expression) will start by selecting all top-level categories (where ‘parent_id’ is NULL) and then recursively join to itself to find subcategories.

Modifying the Hierarchical Data

Updating the hierarchical structure can be done with a simple UPDATE statement:

UPDATE categories
SET parent_id = 
WHERE id = ;

This will change the parent category of a given subcategory. To delete a category and automatically delete its subcategories due to the ON DELETE CASCADE constraint, use:

DELETE FROM categories WHERE id = ;

Extra care should be taken when deleting records as it will remove all child categories as well.

Best Practices and Performance Considerations

While the adjacency list model is easy to understand and manage, it can become inefficient for very deep or very broad trees due to potentially large recursive queries. For cases where performance becomes an issue, you might consider alternate structures such as the ‘materialized path’ or the ‘nested set’ model, which are beyond the scope of this tutorial.

Maintaining indexes can also improve performance significantly. Always have an index on the ‘parent_id’ column if you’re frequently retrieving hierarchical data.

Conclusion

In this tutorial, we’ve covered how you can manage categories and subcategories within one PostgreSQL table. The recursive querying abilities of PostgreSQL make it particularly well-suited for working with hierarchical data, such as category structures.

Remember that is it important to structure your data according to your application’s needs, considering factors such as the depth of the hierarchy, read vs. write ratios, and performance requirements.

With appropriate indexing and prudent design choices, managing a category-subcategory hierarchy within a single PostgreSQL table can be an effective solution for many applications.