Однією з найкорисніших функцій (окрім типу даних JSON) у MySQL 8.0 виявилися генеровані поля. У генерованому полі значення формується автоматично, що дозволяє в деяких випадках спростити SQL-запити, а в інших — позбутися тригерів. Наприклад, у вас є таблиця зі списком товарів у рахунку, в якій є поля з ціноюprice) та кількістю товаруquantity), а також поле із сумоюtotal), значення якого обчислюється в тригері. Поле із сумою займає місце на диску та вимагає використання тригера. З появою MySQL 8.0 поле total можна зробити динамічним. Воно не займатиме місця і завжди буде актуальним.
Приклад створення таблиці з полем, що генерується:
CREATE TABLE invoice_item (
`price` decimal(10,2) unsigned NOT NULL DEFAULT 0,
`quantity` decimal(10,4) unsigned NOT NULL DEFAULT 0,
`total` decimal(10,2) unsigned GENERATED ALWAYS AS (ROUND(quantity * price, 2)) VIRTUAL
)
Формула, яка вказується для генерованих полів, може містити математичні оператори та функції MySQL. З огляду на появу типу даних JSON, у генеровані поля можна виносити значення параметрів із даних JSON, за якими згодом можна здійснювати пошук. Наприклад, у вас є таблиця, в якій зберігаються транзакції від різних платіжних систем. Кожна система при отриманні платежу повертає свій набір даних, який рідко використовується, але потрібен для пошуку за номером банківської картки:
CREATE TABLE transactions (
`response` JSON,
`card` VARCHAR(20) GENERATED ALWAYS AS (response->"$.card_mask") STORED,
INDEX card_idx (card)
)Для генерованого поля вказувати NOT NULL потрібно після визначення GENERATED ALWAYS, а не перед ним.
Незважаючи на те, що InnoDB підтримує індексування віртуальних полів, що генеруються, я б рекомендував у таких випадках зберігати обчислюване поле в таблиці; для цього слід використовувати ключове слово VIRTUAL замінюється на STORED.
Додавання генерованого поля у великих таблицях відбувається дуже швидко, оскільки не вимагає повного перетворення таблиці, як це відбувається зі звичайними полями. Тому сміливо додавайте такі поля у величезних таблицях.
Приклад додавання поля, що генерується:
ALTER TABLE table_name
ADD COLUMN `card` DECIMAL(10,2) GENERATED ALWAYS AS (ROUND(price * quantity, 2)) VIRTUAL;Формулу та тип поля, що використовується для генерації, можна переглянути в таблиці INFORMATION_SCHEMA.COLUMNS:
SELECT column_name, generation_expression, extra
FROM INFORMATION_SCHEMA.COLUMNS
WHERE
table_name='$table_name' and
table_schema='$db_name'
ORDER BY ordinal_positionНедоліком використання генерованих полів є неможливість швидко створювати копії таблиць. Наприклад, запит:
CREATE TABLE `transactions_backup` LIKE `transactions`;
INSERT INTO `transactions_backup`
SELECT * FROM `transactions`;Поверне помилку «The value specified for generated column 'response' in table 'transactions' is not allowed».
Для таких таблиць доводиться вказувати список полів, які не генеруються. У цьому допомагає запит до бази INFORMATION_SCHEMA:
SELECT GROUP_CONCAT('`', column_name, '`' ORDER BY ordinal_position SEPARATOR ', ' )
FROM INFORMATION_SCHEMA.COLUMNS
WHERE
table_name='$table_name' and
table_schema='$db_name' and
generation_expression=''