Ответ
Да, использовал генерируемые (generated) колонки в MySQL для упрощения логики приложения и обеспечения целостности данных на уровне БД.
Пример из реального проекта (MySQL 8.0):
У нас была таблица invoice_items для хранения позиций в счете. Вместо того чтобы вычислять итоговую сумму (total) в коде PHP при каждом чтении, мы вынесли это в генерируемую колонку.
CREATE TABLE invoice_items (
id INT PRIMARY KEY AUTO_INCREMENT,
invoice_id INT NOT NULL,
unit_price DECIMAL(10, 2) NOT NULL,
quantity INT NOT NULL,
discount_percent DECIMAL(5, 2) DEFAULT 0.00,
-- VIRTUAL колонка (вычисляется на лету)
discount_amount DECIMAL(10, 2) GENERATED ALWAYS AS (unit_price * quantity * discount_percent / 100) VIRTUAL,
-- STORED колонка (сохраняется на диске, можно индексировать)
line_total DECIMAL(10, 2) GENERATED ALWAYS AS ((unit_price * quantity) - discount_amount) STORED,
INDEX idx_invoice_id (invoice_id),
FOREIGN KEY (invoice_id) REFERENCES invoices(id) ON DELETE CASCADE
);
Как это работает в PHP-приложении:
// При вставке данных указываем только базовые поля
$stmt = $pdo->prepare("INSERT INTO invoice_items (invoice_id, unit_price, quantity, discount_percent) VALUES (?, ?, ?, ?)");
$stmt->execute([$invoiceId, 99.99, 5, 10.00]);
// При выборке `line_total` и `discount_amount` доступны как обычные поля
$stmt = $pdo->query("SELECT id, unit_price, quantity, line_total FROM invoice_items WHERE invoice_id = $invoiceId");
$items = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($items as $item) {
echo "Item ID: {$item['id']}, Total: {$item['line_total']}n";
// line_total уже вычислен и готов к использованию
}
Практические соображения:
- VIRTUAL vs STORED: Использовал
VIRTUAL(по умолчанию) для значений, которые редко используются или часто обновляются, чтобы не тратить место на диске.STOREDприменял для колонок, которые часто используются вWHEREилиORDER BY, так как их можно проиндексировать, что значительно ускоряет поиск. - Переносимость: Хотя синтаксис похож в PostgreSQL (
GENERATED ALWAYS AS) и SQLite, в старых версиях MySQL (< 5.7) эта функция недоступна. Поэтому всегда проверял версию СУБД перед проектированием схемы. - Альтернатива в коде: В проектах, где нельзя было полагаться на генерируемые колонки (например, при использовании облачных БД с ограничениями), реализовывал аналогичную логику через геттеры в моделях Eloquent/Doctrine или триггеры в БД.