CREATE TABLE my_app.person
(
person_id smallserial NOT NULL,
first_name character varying(50),
last_name character varying(50),
full_name character varying(100) generated always as (concat(first_name, ' ', last_name)) STORED,
birth_date date,
created_timestamp timestamp default current_timestamp,
PRIMARY KEY (person_id)
);
Error: generation expression is not immutable
The goal is to populate the first name and last into the full name column.
A generated column is a special column that is always computed from other columns. Thus, it is for columns what a view is for tables. There are two kinds of generated columns: stored and virtual.
Syntax. The syntax to add a column in a table in PostgreSQL (using the ALTER TABLE statement) is: ALTER TABLE table_name ADD new_column_name column_definition; table_name.
The Postgres IF NOT EXISTS syntaxFirst, we specify the name of the table to which we want to add a column. We supply the IF NOT EXISTS option after the ADD COLUMN clause, and then we specify the name of the column and its data type.
Introduction. “Generated Always Column”: are columns, table fields, filled by DB2 engine: something like columns with a default value but in this case always with the default value not only when users don't pass a value.
The concat()
function is not IMMUTABLE
(only STABLE
) because it can invoke datatype output functions (like timestamptz_out
) that depend on locale settings. Tom Lane (core developer) explains it here.
And first_name || ' ' || last_name
is not equivalent to concat(first_name, ' ', last_name)
while at least one column can be NULL
.
Detailed explanation:
To make it work, exactly the way you demonstrated:
CREATE TABLE person (
person_id smallserial PRIMARY KEY
, first_name varchar(50)
, last_name varchar(50)
, full_name varchar(101) GENERATED ALWAYS AS
(CASE WHEN first_name IS NULL THEN last_name
WHEN last_name IS NULL THEN first_name
ELSE first_name || ' ' || last_name END) STORED
, ...
);
db<>fiddle here
The CASE
expression is as fast as it gets - substantially faster than multiple concatenation and function calls. And exactly correct.
Or, if you know what you are doing and have the necessary privileges, create an IMMUTABLE
concat function as demonstrated here (to replace the CASE
expression):
Aside: full_name
needs to be varchar(101)
(50+50+1) to make sense. Or just use text
columns instead. See:
The best solution depends on how you plan to deal with NULL values (and empty strings) exactly. I would probably not add a generated column. That's typically more expensive and error prone overall than to concatenate the full name on the fly. Consider a view, or a function encapsulating the concatenation logic.
Related:
This works with the ||
operator:
CREATE TABLE person (
person_id smallserial NOT NULL,
first_name character varying(50),
last_name character varying(50),
full_name character varying(100) generated always as (first_name || ' ' || last_name) STORED,
birth_date date,
created_timestamp timestamp default current_timestamp,
PRIMARY KEY (person_id)
);
I am not sure of the technical reasons why concat()
is considered mutable, but ||
is not.
If you want to handle NULL
values in the columns, then it is a little more complicated. I might recommend:
trim(both ' ' from
(' ' || coalesce(first_name, '') || ' ' || coalesce(last_name, '')
)
)
Of course, this isn't exactly the same as your expression, because it removes spaces from the beginning and end of the names.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With