问题:
假设有一张表
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),需要调整表的唯一性约束。以下是具体步骤:
删除原有的唯一约束:由于当前唯一约束包含
deleted
字段,导致无法插入重复的(a, b, c, 0)记录。ALTER TABLE test_0216 DROP INDEX uc_test;
添加自增主键(可选,但推荐用于表结构规范化):
ALTER TABLE test_0216 ADD COLUMN id INT AUTO_INCREMENT PRIMARY KEY FIRST;
插入新数据:
INSERT INTO test_0216 (aa, bb, cc, deleted) VALUES ('a', 'b', 'c', '0');
注意:移除唯一约束后,表可能允许不符合业务规则的重复数据。如果业务需要新的约束(如仅对未删除记录保持唯一),可通过虚拟列和条件唯一索引实现:
添加虚拟列:
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;
创建条件唯一索引:
CREATE UNIQUE INDEX uc_new_unique ON test_0216 (unique_key);
此方案允许deleted=0
时aa, bb, cc
唯一,而deleted=1
时不限制,确保数据完整性的同时满足插入需求。
为什么是虚拟列?
虚拟列(Generated Column) 是数据库中的一种特殊列,它的值不是通过直接插入数据获得的,而是通过表中其他列的值 动态计算生成 的。虚拟列的值在插入或更新数据时根据预定义的规则自动计算,无需手动维护。
虚拟列的特点
两种类型:
VIRTUAL
(虚拟):
仅在查询时动态计算值,不占用物理存储空间。
例如:SELECT
时才会根据规则生成值。STORED
(存储):
值在插入或更新时计算并实际存储在表中,会占用磁盘空间。
例如:类似普通列,但值由公式生成。生成规则:
通过表达式或函数定义,例如CONCAT(aa, bb)
或CASE WHEN deleted='0' THEN ...
。不可直接写入:
虚拟列的值由数据库自动生成,用户无法直接插入或修改。
在之前的例子中,为什么需要虚拟列?
用户原有唯一约束 uc_test
包含 deleted
字段,导致无法插入两条 (a, b, c, 0)
记录。
但业务需求是:
- 未删除的数据(
deleted=0
):要求(aa, bb, cc)
唯一。 - 已删除的数据(
deleted=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;
当
deleted=0
时,生成aa|bb|cc
的字符串(如a|b|c
)。当
deleted=1
时,值为NULL
。创建唯一索引:
CREATE UNIQUE INDEX uc_new_unique ON test_0216 (unique_key);
唯一索引会忽略
NULL
值,因此允许deleted=1
时有重复的(aa, bb, cc)
。当
deleted=0
时,unique_key
必须唯一,否则违反唯一约束。
虚拟列的优势
- 动态约束:
实现条件唯一性(如仅对未删除记录约束)。 - 节省存储:
使用VIRTUAL
类型时,不占用磁盘空间。 - 简化逻辑:
避免在应用层手动维护复杂规则。
虚拟列的常见用途
- 组合字段的唯一约束(如
aa|bb|cc
)。 - 条件索引(如仅对有效数据建立唯一性)。
- 简化查询(如预计算字段
total_price = quantity * price
)。
通过虚拟列,可以更灵活地设计表结构,同时保持数据一致性。