Working with literal values, you can achieve the same with a subquery:
INSERT INTO my_table (a,b,c,d)
SELECT *, CASE WHEN c<0 THEN a+b ELSE a-b END AS d
FROM (
VALUES
(3,2, 1) -- input values here, once
, (3,2,-1) -- multiple rows if need be
) sub(a,b,c);
This works without explicit type casts for basic types like integer
or text
as the assumed default types for the literals happen to match. You may need explicit data type casts for other data types in a separate VALUES
expression. See:
More fundamentally, avoid the problem by not storing redundant data in the table to begin with.
CREATE TABLE my_table (
a int
, b int
, c int
-- NO d; will be computed on the fly
);
INSERT
is back to basics:
INSERT INTO my_table (a,b,c) VALUES
(3,2, 1)
, (3,2, 0)
, (3,2,-1)
;
The additional column d
can cheaply be computed on the fly. Typically much cheaper than reading more data pages due to increased storage:
SELECT *, CASE WHEN c<0 THEN a+b ELSE a-b END AS d
FROM my_table;
You might persist the logic in a VIEW
:
CREATE VIEW my_view AS
SELECT *, CASE WHEN c<0 THEN a+b ELSE a-b END AS d
FROM my_table;
SELECT * FROM my_view;
If you positively need the computed value stored (like when computation is relatively expensive, or you do other things with the column), use a STORED
generated column (Postgres 12+):
ALTER TABLE my_table
ADD column d int GENERATED ALWAYS AS (CASE WHEN c<0 THEN a+b ELSE a-b END) STORED;
SELECT * my_table;
fiddle
See: