Computed Columns in PostgreSQL: From Historical Workarounds to Native Support

Dec 02, 2025 · Programming · 17 views · 7.8

Keywords: PostgreSQL | Computed Columns | Generated Columns | Database Design | Performance Optimization

Abstract: This technical article provides a comprehensive analysis of computed columns (also known as generated, virtual, or derived columns) in PostgreSQL. It systematically examines the native STORED generated columns introduced in PostgreSQL 12, compares implementations with other database systems like SQL Server, and details various technical approaches for emulating computed columns in earlier versions through functions, views, triggers, and expression indexes. With code examples and performance analysis, the article demonstrates the advantages, limitations, and appropriate use cases for each implementation method, offering valuable insights for database architects and developers.

The Evolution of Computed Columns in PostgreSQL

In database system design, computed columns serve as a crucial data abstraction mechanism, allowing developers to define derived columns in table structures based on calculations from other column values. This feature has been implemented in major relational database systems such as SQL Server, MySQL, and Oracle. PostgreSQL has undergone a significant evolution in this area, transitioning from workaround implementations to native support over its development history.

Native Support in PostgreSQL 12 and Later

Starting with PostgreSQL 12, the database introduced native support for STORED generated columns that conform to the SQL standard. This implementation shares conceptual similarities with SQL Server's "computed columns" but employs different syntax and storage strategies. STORED generated columns compute their values during data insertion or updates and physically store the results on disk. This design offers advantages in read performance but increases storage requirements and write overhead.

The following example demonstrates a typical STORED generated column definition:

CREATE TABLE products (
  price    decimal(10,2),
  quantity integer,
  total    decimal(12,2) GENERATED ALWAYS AS (price * quantity) STORED
);

In this example, the total column is defined as the product of price and quantity, with its value automatically computed and stored each time a row is inserted or updated. It's important to note that PostgreSQL currently supports only STORED generated columns, while VIRTUAL generated columns (computed only during queries without storage overhead) have not yet been implemented, representing a difference from some other database systems.

Workaround Implementations in Earlier Versions

For PostgreSQL 11 and earlier versions that lacked native computed column support, developers could employ several technical approaches to achieve similar functionality. Each approach has distinct characteristics suitable for different use cases.

Using Functions with Attribute Notation

PostgreSQL supports a unique syntactic feature called attribute notation, which allows function calls to be written in a form resembling column references. Although this feature originated for historical reasons, it conveniently enables emulation of virtual generated column behavior.

-- Create computation function
CREATE FUNCTION item_total(products) RETURNS decimal(12,2) AS $$
  SELECT $1.price * $1.quantity;
$$ LANGUAGE SQL IMMUTABLE;

-- Query using attribute notation
SELECT p.price, p.quantity, p.item_total FROM products p;

The primary limitation of this approach is that computed columns are not automatically included in SELECT * query results and must be explicitly listed. Additionally, computation functions should be marked as IMMUTABLE (for deterministic calculations) to enable proper query plan optimization by the database engine.

Expression Index Optimization

When computed columns are frequently used in query conditions or join operations, creating expression indexes can significantly improve performance. This approach is particularly suitable for scenarios involving frequent queries but infrequent updates.

-- Create index on computation expression
CREATE INDEX idx_products_total ON products ((price * quantity));

-- Query utilizing expression index
SELECT * FROM products WHERE (price * quantity) > 1000;

Expression indexes enable the database to use index scans for queries involving computed column conditions, dramatically improving query performance. However, indexes consume additional storage space and incur maintenance overhead during data updates.

Alternative Implementation Approaches

View-Based Approach

Using views represents another common method for implementing computed column functionality. Views can encapsulate complex computation logic and provide a unified query interface.

-- Create view with computed column
CREATE VIEW products_with_total AS
SELECT id, price, quantity, 
       (price * quantity) AS total
FROM products;

-- Query the view
SELECT * FROM products_with_total WHERE total > 1000;

The view approach offers advantages in centralized management and maintenance simplicity of computation logic. Performance can be further optimized by creating expression indexes on view queries. However, views may have limitations in certain complex update operations, and computations are performed during each query execution.

Trigger-Based Approach

For scenarios requiring persistently stored computed values, triggers can automatically compute and store results during data insertion or updates. This method functionally resembles STORED generated columns most closely.

-- Add column for storing computed result
ALTER TABLE products ADD COLUMN total decimal(12,2);

-- Create trigger function for updates
CREATE FUNCTION update_product_total() RETURNS TRIGGER AS $$
BEGIN
  NEW.total := NEW.price * NEW.quantity;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Create trigger
CREATE TRIGGER trg_product_total
BEFORE INSERT OR UPDATE ON products
FOR EACH ROW
EXECUTE FUNCTION update_product_total();

The trigger approach provides maximum flexibility for handling complex computation logic and business rules. However, triggers increase database complexity, may impact performance, and require additional maintenance efforts.

Materialized View Approach

PostgreSQL 9.3 and later versions support materialized views, which physically store query results. Materialized views are particularly suitable for scenarios involving expensive computations with infrequent data updates.

-- Create materialized view
CREATE MATERIALIZED VIEW product_summary AS
SELECT product_id, 
       SUM(price * quantity) AS total_sales,
       AVG(price) AS avg_price
FROM sales
GROUP BY product_id;

-- Refresh materialized view data
REFRESH MATERIALIZED VIEW product_summary;

Materialized views offer significant advantages in query performance but require manual or scheduled refreshing to maintain data currency. In versions prior to PostgreSQL 9.3, similar functionality could be implemented through custom tables and management logic.

Performance and Applicability Analysis

Different computed column implementation approaches exhibit distinct performance characteristics and suitability for various scenarios:

  1. STORED Generated Columns: Suitable for scenarios with frequent reads, relatively simple computations, and high data currency requirements. Storage overhead and write performance are primary considerations.
  2. Function-Based Emulation: Appropriate for complex computation logic requiring flexible definition and infrequent queries. Avoids storage overhead but requires recomputation on each query.
  3. View-Based Approach: Ideal for logic encapsulation and query simplification needs. Combined with expression indexes, it balances performance and flexibility.
  4. Trigger-Based Approach: Suitable for scenarios requiring data consistency guarantees and complex business logic. Offers maximum control but increases system complexity.
  5. Materialized View Approach: Appropriate for analytical queries with expensive computations and infrequent data updates. Provides optimal query performance but requires refresh strategy management.

Best Practice Recommendations

When selecting a computed column implementation approach, consider the following factors:

In practical applications, combining multiple approaches often proves necessary to address diverse requirements. For example, STORED generated columns might handle core business computations, while materialized views support analytical queries, with functions encapsulating complex computation logic.

Future Developments

As PostgreSQL continues to evolve, computed column functionality is expected to further mature. The community is actively discussing and developing support for VIRTUAL generated columns, which would provide more flexible computed column implementation options. Additionally, integration of generated columns with advanced features like partitioned tables and parallel query execution represents a future development direction.

For database architects and developers, understanding the technical principles and appropriate use cases of different computed column implementation approaches enables more informed technology selection and facilitates the construction of efficient, maintainable database application systems.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.