Одной из наиболее полезных функций (помимо типа данных 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=''