Implementing Dynamic PIVOT Tables in PostgreSQL with PL/pgSQL

Shiv IyerShiv Iyer
3 min read

Dynamically pivoting tables in PostgreSQL involves transforming rows into columns dynamically, typically because the number of columns can change based on the data. Unlike some other RDBMS like SQL Server, PostgreSQL doesn't have a built-in PIVOT function, but you can achieve dynamic pivoting using the crosstab function from the tablefuncextension along with dynamic SQL in PL/pgSQL. Here's a step-by-step approach:

Step 1: Install the tablefunc Extension

First, ensure the tablefunc extension is installed in your database as it contains the crosstab function needed for pivoting.

CREATE EXTENSION IF NOT EXISTS tablefunc;

Step 2: Identify Your Pivot Query

Determine the query you need to pivot. For example, let's say you have sales data in a table sales(year, product, amount) and you want to pivot this data to show amounts by product across years.

Step 3: Write a Dynamic PL/pgSQL Function

Because the number of years might change, you'll need to write a dynamic PL/pgSQL function that constructs and executes a dynamic crosstab query.

CREATE OR REPLACE FUNCTION dynamic_pivot() RETURNS TABLE (...) AS $$
DECLARE
    sql TEXT;
    year_list TEXT = '';
BEGIN
    -- Dynamically build the list of years for column headers
    SELECT STRING_AGG(DISTINCT quote_literal(year) || ' AS ' || quote_ident('year_' || year), ', ')
    INTO year_list
    FROM sales
    ORDER BY year;

    -- Construct the dynamic SQL for crosstab
    sql := 'SELECT * FROM crosstab(
              ''SELECT product, year, amount FROM sales ORDER BY 1,2'',
              ''SELECT DISTINCT year FROM sales ORDER BY 1''
           ) AS ct(product TEXT, ' || year_list || ');';

    -- Return the result of the dynamic crosstab query
    RETURN QUERY EXECUTE sql;
END;
$$ LANGUAGE plpgsql;

Note: The RETURNS TABLE (...) part of the function definition needs to be adjusted to match the expected columns of your pivoted result. This includes the static columns (e.g., product TEXT) and dynamic columns for each year which you've prepared in year_list.

Considerations:

  1. Dynamic Columns: In the function, you need to dynamically define the return type of your function or use a generic return type like SETOF RECORD and deal with the column definition at the call site. Because PostgreSQL functions need a defined return type, dynamic columns present a challenge.

  2. SQL Injection: Dynamic SQL can be susceptible to SQL injection. Ensure that any user inputs are properly sanitized, and use PostgreSQL's format(), quote_ident(), and quote_literal() functions to construct dynamic queries safely.

  3. Usage: Call the function in your SQL query like so:

SELECT * FROM dynamic_pivot();

This approach allows you to dynamically pivot data in PostgreSQL. However, due to the dynamic nature of the return type, it's a bit more complex to implement and use compared to static pivoting. It requires careful construction of the dynamic SQL statement and handling of the function's return type. Always test thoroughly to ensure the function behaves as expected with your data.

Read more PostgreSQL Blogs.

0
Subscribe to my newsletter

Read articles from Shiv Iyer directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Shiv Iyer
Shiv Iyer

Over two decades of experience as a Database Architect and Database Engineer with core expertize in Database Systems Architecture/Internals, Performance Engineering, Scalability, Distributed Database Systems, SQL Tuning, Index Optimization, Cloud Database Infrastructure Optimization, Disk I/O Optimization, Data Migration and Database Security. I am the founder CEO of MinervaDB Inc. and ChistaDATA Inc.