Reputation: 959
We're implementing an e-commerce data model and struggle to decide between Postgres generated columns, Hasura computed fields, and Postgres views.
As a simplification, we have two tables:
items
------------
id
order_id
unit_price
quantity
orders
------------
id
We now want to add total_price
to the items
table (unit_price * quantity
), and total_price
to orders (sum
of total_price
of all items
in the order).
In the first case, we used a Postgres generated column. The benefit here seems to be that it is generated once, and stored, rather than rerun on each query like a Hasura computed field would be.
Is this the right choice?
items
------------
id
order_id
unit_price
quantity
total_price : Postgres generated column : (unit_price * quantity)
CREATE TABLE orders (
...,
total_price integer GENERATED ALWAYS AS (unit_price * quantity) STORED
);
In the second case, we can't use a generated column, as it cannot reference fields from a different table.
Instead, we can solve this using a Hasura computed field:
orders
------------
id
total_price : Hasura computed field : SUM(items.total_price)
CREATE FUNCTION calculate_order_total_price(orders_row orders)
RETURNS INTEGER AS $$
SELECT CAST(SUM(total_price) AS INTEGER)
FROM items
WHERE order_id = orders_row.id
$$ LANGUAGE sql STABLE;
Alternatively, we can create a Postgres view:
vw_orders
------------
orders.id
total_price : SELECT SUM(items.total_price)
CREATE VIEW vw_orders AS
SELECT orders.id,
(SELECT sum(items.total_price) AS sum
FROM items
WHERE (items.order_id = orders.id)) AS total_price
FROM orders;
The Hasura computed field solution has the detriment that total_price
is only exposed in GraphQL queries, so we could not use it in SQL.
The Postgres view solution does not seem to have any problems, at first sight.
Are we missing something?
Why would one ever use a Hasura computed field over Postgres generated fields or views?
Is there a comparison table or flow chart somewhere that would help us decide which approach is best in each particular situation?
Finally, for all of these, we could of course also use Postgres triggers, Hasura event triggers, and Hasura actions... when would those be the appropriate solutions?
Cheers!
Upvotes: 5
Views: 2054
Reputation: 246483
For this specific case I would go with a PostgreSQL view, because the computation involved is quite cheap (a single multiplication). It may even be that the savings in storage space and the resulting speed gain in sequential scans outweigh the downside of having to perform the computation whenever the value is needed. Recent PostgreSQL versions can also use just-in-time compilation (JIT) to make such expressions cheaper to compute (for bigger queries).
Generally speaking, the best strategy will probably depend on the computational intensity of the expression. If that is high, and the rows are more often read than written, it will probably make sense to use a generated column.
You could run a little benchmark and examine the difference. In your case, I don't think that the impact will be very high, and it may be best to choose the implementation that is simplest or most readable, because that improves maintainability.
Upvotes: 2