mysql 生成列
MySQL 的生成列(Generated Column)是一个非常有用的特性,让我来详细介绍:
基本语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 创建表时定义生成列
CREATE TABLE example (
id INT PRIMARY KEY,
first_name VARCHAR(50),
last_name VARCHAR(50),
-- 虚拟列
full_name VARCHAR(101) AS (CONCAT(first_name, ' ', last_name)),
-- 存储列
full_name_stored VARCHAR(101) AS (CONCAT(first_name, ' ', last_name)) STORED
);
-- 在现有表中添加生成列
ALTER TABLE example
ADD COLUMN age INT AS (YEAR(CURRENT_DATE) - YEAR(birth_date)) STORED;
两种类型
- 虚拟列(VIRTUAL)
1 2
-- 默认是 VIRTUAL ADD COLUMN total_amount DECIMAL(10,2) AS (quantity * price)
- 不占用实际存储空间
- 每次查询时实时计算
- 适合简单计算且不频繁查询的场景
- 存储列(STORED)
1
ADD COLUMN total_amount DECIMAL(10,2) AS (quantity * price) STORED
- 物理存储在表中
- 在数据变更时计算并存储
- 适合复杂计算或频繁查询的场景
常见使用场景
- 日期处理
1 2 3 4 5 6 7 8 9 10 11 12
CREATE TABLE orders ( id INT PRIMARY KEY, created_at DATETIME, -- 年月 ym VARCHAR(7) AS (DATE_FORMAT(created_at, '%Y-%m')) STORED, -- 年份 year INT AS (YEAR(created_at)) STORED, -- 月份 month INT AS (MONTH(created_at)) STORED, -- 季度 quarter INT AS (QUARTER(created_at)) STORED );
- 数据转换
1 2 3 4 5 6
CREATE TABLE products ( id INT PRIMARY KEY, price_cents INT, -- 转换为元 price_yuan DECIMAL(10,2) AS (price_cents / 100) STORED );
- 计算字段
1 2 3 4 5 6 7 8 9
CREATE TABLE order_items ( id INT PRIMARY KEY, quantity INT, unit_price DECIMAL(10,2), -- 计算总价 total_price DECIMAL(10,2) AS (quantity * unit_price) STORED, -- 计算税额 tax_amount DECIMAL(10,2) AS (quantity * unit_price * 0.13) STORED );
- 字符串处理
1 2 3 4 5 6 7 8
CREATE TABLE users ( id INT PRIMARY KEY, email VARCHAR(255), -- 提取邮箱域名 email_domain VARCHAR(255) AS (SUBSTRING_INDEX(email, '@', -1)) STORED, -- 转换大小写 email_lower VARCHAR(255) AS (LOWER(email)) STORED );
注意事项
- 限制
1 2 3 4 5
-- 生成列不能: -- 1. 直接被插入数据 -- 2. 直接被更新 -- 3. 包含非确定性函数(如 RAND()) -- 4. 包含子查询
- 索引使用
1 2
-- 可以在生成列上创建索引 CREATE INDEX idx_ym ON orders(ym);
- 性能考虑
1 2 3
-- 选择 VIRTUAL 还是 STORED: -- 1. 频繁查询但数据变更少 -> STORED -- 2. 简单计算且查询不频繁 -> VIRTUAL
- 实际应用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
CREATE TABLE sales ( id INT PRIMARY KEY, amount DECIMAL(10,2), created_at DATETIME, -- 用于报表统计 year_month VARCHAR(7) AS (DATE_FORMAT(created_at, '%Y-%m')) STORED, -- 用于季度分析 quarter AS (CONCAT(YEAR(created_at), 'Q', QUARTER(created_at))) STORED, -- 用于金额分类 amount_level VARCHAR(20) AS ( CASE WHEN amount < 100 THEN 'low' WHEN amount < 1000 THEN 'medium' ELSE 'high' END ) STORED );
生成列的使用可以:
- 简化查询逻辑
- 提高代码可维护性
- 优化查询性能
- 确保数据一致性
1
2
3
4
5
6
7
-- 根据datetime 生成索引常用的列
ALTER TABLE factory_affair_record
ADD COLUMN year INT GENERATED ALWAYS AS (YEAR(time_stamp)) STORED,
ADD COLUMN month VARCHAR(10) AS (CONCAT(CAST(YEAR(time_stamp) AS CHAR), '-', LPAD(CAST(MONTH(time_stamp) AS CHAR), 2, '0'))) STORED,
ADD COLUMN week_num INT AS (WEEK(time_stamp, 3)) STORED,
ADD COLUMN day DATE GENERATED ALWAYS AS (DATE(time_stamp)) STORED,
ADD COLUMN year_week INT GENERATED ALWAYS AS (YEARWEEK(time_stamp)) STORED;
本文由作者按照 CC BY 4.0 进行授权