mysql虚拟列的应用场景

问题: 假设有一张表 CREATE TABLE `test_0216` ( `aa` varchar(255) DEFAULT NULL, `bb` varchar(255) DEFAULT NULL, `cc` varchar(255) DEFAULT NULL, `deleted

问题:

假设有一张表
CREATE TABLE `test_0216` (
  `aa` varchar(255) DEFAULT NULL,
  `bb` varchar(255) DEFAULT NULL,
  `cc` varchar(255) DEFAULT NULL,
  `deleted` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
改表没有主键,且有唯一性约束 constraint uc_test unique(aa,bb,cc,deleted);
目前表中有数据如下
a   b   c   0
a   b   c   1
请问怎么改变表结果,使得历史数据保留且插入一条新的数据 a  b  c  0

解决:

为了实现在保留历史数据的同时插入新数据(a, b, c, 0),需要调整表的唯一性约束。以下是具体步骤:

  1. 删除原有的唯一约束:由于当前唯一约束包含deleted字段,导致无法插入重复的(a, b, c, 0)记录。

    ALTER TABLE test_0216 DROP INDEX uc_test;
    
  2. 添加自增主键(可选,但推荐用于表结构规范化):

    ALTER TABLE test_0216 ADD COLUMN id INT AUTO_INCREMENT PRIMARY KEY FIRST;
    
  3. 插入新数据

    INSERT INTO test_0216 (aa, bb, cc, deleted) VALUES ('a', 'b', 'c', '0');
    

注意:移除唯一约束后,表可能允许不符合业务规则的重复数据。如果业务需要新的约束(如仅对未删除记录保持唯一),可通过虚拟列和条件唯一索引实现:

  1. 添加虚拟列

    ALTER TABLE test_0216 ADD COLUMN unique_key VARCHAR(255) GENERATED ALWAYS AS (CASE WHEN deleted = '0' THEN CONCAT_WS('|', aa, bb, cc) ELSE NULL END) VIRTUAL;
    
  2. 创建条件唯一索引

    CREATE UNIQUE INDEX uc_new_unique ON test_0216 (unique_key);
    

此方案允许deleted=0aa, bb, cc唯一,而deleted=1时不限制,确保数据完整性的同时满足插入需求。

为什么是虚拟列?

虚拟列(Generated Column) 是数据库中的一种特殊列,它的值不是通过直接插入数据获得的,而是通过表中其他列的值 动态计算生成 的。虚拟列的值在插入或更新数据时根据预定义的规则自动计算,无需手动维护。


虚拟列的特点

  1. 两种类型

  2. VIRTUAL(虚拟)
    仅在查询时动态计算值,不占用物理存储空间
    例如:SELECT 时才会根据规则生成值。

  3. STORED(存储)
    值在插入或更新时计算并实际存储在表中,会占用磁盘空间。
    例如:类似普通列,但值由公式生成。

  4. 生成规则
    通过表达式或函数定义,例如 CONCAT(aa, bb)CASE WHEN deleted='0' THEN ...

  5. 不可直接写入
    虚拟列的值由数据库自动生成,用户无法直接插入或修改。


在之前的例子中,为什么需要虚拟列?

用户原有唯一约束 uc_test 包含 deleted 字段,导致无法插入两条 (a, b, c, 0) 记录。
但业务需求是:

  • 未删除的数据(deleted=0:要求 (aa, bb, cc) 唯一。
  • 已删除的数据(deleted=1:允许重复。

解决方法

  1. 添加虚拟列 unique_key
    根据 deleted 的值动态生成唯一标识:

    ALTER TABLE test_0216 
    ADD COLUMN unique_key VARCHAR(255) 
    GENERATED ALWAYS AS (
       CASE WHEN deleted = '0' THEN CONCAT_WS('|', aa, bb, cc) 
       ELSE NULL 
       END
    ) VIRTUAL;
    
  2. deleted=0 时,生成 aa|bb|cc 的字符串(如 a|b|c)。

  3. deleted=1 时,值为 NULL

  4. 创建唯一索引

    CREATE UNIQUE INDEX uc_new_unique ON test_0216 (unique_key);
    
  5. 唯一索引会忽略 NULL 值,因此允许 deleted=1 时有重复的 (aa, bb, cc)

  6. deleted=0 时,unique_key 必须唯一,否则违反唯一约束。


虚拟列的优势

  1. 动态约束
    实现条件唯一性(如仅对未删除记录约束)。
  2. 节省存储
    使用 VIRTUAL 类型时,不占用磁盘空间。
  3. 简化逻辑
    避免在应用层手动维护复杂规则。

虚拟列的常见用途

  1. 组合字段的唯一约束(如 aa|bb|cc)。
  2. 条件索引(如仅对有效数据建立唯一性)。
  3. 简化查询(如预计算字段 total_price = quantity * price)。

通过虚拟列,可以更灵活地设计表结构,同时保持数据一致性。

LICENSED UNDER CC BY-NC-SA 4.0
Comment