Згенерована колонка обчислюється базою з інших колонок того ж рядка - як вираз у формулі таблиці. Записати її значення напряму не можна.
CREATE TABLE order_items (
price numeric(10, 2) NOT NULL,
quantity int NOT NULL,
total numeric(12, 2) GENERATED ALWAYS AS (price * quantity) STORED
);
ALTER TABLE users
ADD COLUMN email_normalized text GENERATED ALWAYS AS (lower(trim(email))) VIRTUAL;
Два види:
STORED- обчислюється при вставці чи оновленні й зберігається на диску, як звичайна колонка. Читання безкоштовне; можна індексувати.VIRTUAL(PostgreSQL 18+) - не займає місця, обчислюється при читанні, як подання. У PostgreSQL 18 це вид за замовчуванням.
Навіщо:
- Похідні значення завжди узгоджені: сума позиції, нормалізований email, повне ім'я - неможливо забути оновити в одному з місць коду.
- Індекс для пошуку:
tsvectorдля повнотекстового пошуку, нормалізоване значення для унікальності без урахування регістру - збережену колонку можна проіндексувати звичайним індексом. - Витягти поле з JSON у звичайну колонку для фільтрів і індексів.
Обмеження:
- Вираз має бути детермінованим (immutable): без
now(), випадкових чисел, запитів до інших таблиць. - Не можна посилатися на інші згенеровані колонки чи колонки інших таблиць.
- Зміна виразу збереженої колонки - переписування таблиці.
Альтернативи: індекс за виразом (якщо значення потрібне лише для пошуку, а не для читання), тригер (для складнішої логіки з іншими таблицями), обчислення в застосунку (якщо значення не потрібне в базі).