Implementing Dynamic PIVOT Tables in PostgreSQL with PL/pgSQL
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 tablefunc
extension 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:
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.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()
, andquote_literal()
functions to construct dynamic queries safely.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.
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.