Зміст
    18.09.2020

    Однією з найкорисніших функцій (окрім типу даних 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=''