GBase 8s:主键约束、唯一约束与唯一索引该如何区分?
在数据库设计中,保证数据的唯一性是一项基础需求。GBase 8s 提供了三种实现手段:主键约束(PRIMARY KEY)、唯一约束(UNIQUE) 和 唯一索引(UNIQUE INDEX)。
三者在功能上高度相似,容易混淆,但在约束规则、实现机制和使用场景上存在明确差异。本文逐一梳理三者的定义与区别,并通过实际操作进行验证。
一、主键约束(PRIMARY KEY)
主键用于唯一标识表中的每一条记录,其主要特性包括:
可以定义一列或多列为主键,但一个表上只能有一个主键;
主键列上没有任何两行具有相同值,且不允许为空(NULL);
不建议更新主键;
主键可作为外键被引用,唯一索引不可。
二、唯一约束(UNIQUE)
唯一约束用于限制主键以外列上数据的唯一性,可作为访问某行的可选手段:
一个表上可以放置多个唯一约束;
只要保证唯一,该列的值就可以更新;
表中任意两行在指定列上不允许有相同值,但允许存在空值(NULL)。
三、唯一索引(UNIQUE INDEX)
唯一索引从索引层面保证键值唯一:
任何生成重复键值的插入或更新尝试都会失败;
只要保证唯一,该列的值就可以更新;
与唯一约束相同,允许存在空值(NULL)。
四、三者之间的区别
1. 唯一约束 vs 主键约束
唯一约束允许列上存在 NULL 值;主键约束的限制更为严格,既不允许重复,也不允许空值。
2. 唯一约束 vs 唯一索引
两者的目的不同:
唯一约束:用于检查数据的正确性;
唯一索引:用于实现数据查询的优化。
在实现机制上:
创建唯一约束时,GBase 8s 会在创建一个 constraint 的同时,创建一个该约束对应的唯一索引;
创建唯一索引只会创建索引本身,不会创建 constraint。
也就是说,唯一约束实际上是通过创建唯一索引来实现的。
3. 核心差异速览

五、实操验证
下面通过一组示例验证三者的行为差异。
1. 创建表并导入基础数据

2. 分别创建主键、唯一约束和唯一索引

通过 oncheck -pt 查看表的索引情况,可以看到三个索引,分别对应主键、唯一约束和唯一索引:

注意:主键和唯一约束对应的索引名称是系统自动生成的(如 102_3、102_4),并非创建时指定的约束名称;而手工创建的唯一索引则保留指定名称 ux_tab1_col3。
3. 插入数据测试
① 向主键列插入 NULL:

报错 703: Primary key on table (tab1) has a field with a null key value.,主键列不允许空值。
② 向主键列插入重复值:

报错 268: Unique constraint (gbasedbt.pk_tab1_col1) violated.,因值 3 已存在,违反主键约束。
③ 向唯一约束列插入 NULL(该列已有 NULL 值):

正常插入,不报错,可见在 GBase 8s 中,唯一约束列上的 NULL 值不参与唯一性检查,允许出现多个 NULL。
④ 向唯一约束列插入重复值:

报错 268: Unique constraint (gbasedbt.uk_tab1_col2) violated.,因值 test003 已存在。
⑤ 向唯一索引列插入 NULL(该列已有 NULL 值):

正常插入,不报错,唯一索引列上的 NULL 值同样允许重复,不参与唯一性检查。
⑥ 向唯一索引列插入重复值:

报错 239: Could not insert new row - duplicate value in a UNIQUE INDEX column (Unique Index:ux_tab1_col3).,唯一索引列上的值不允许重复。
⑦ 插入一行全新数据:

插入成功。
4. 删除唯一约束,观察索引变化

再次查看索引情况:

删除唯一约束后,其对应的索引被一并删除,仅剩主键索引和手工创建的唯一索引。这也印证了"唯一约束是通过创建唯一索引来实现的"这一机制。
六、小结
综合上述测试,可以得出以下结论:
如果字段加上 NOT NULL 约束,再加上唯一约束或唯一索引,功能上将等同于主键约束;
唯一约束与唯一索引的唯一性检查行为一致,唯一约束仅仅是比唯一索引多了一个 constraint 定义;
主键约束限制最严格(唯一且非空),一张表只能有一个;唯一约束和唯一索引允许 NULL(且允许多个 NULL),一张表可以创建多个。
实践建议:数据的唯一性规则属于业务逻辑的一部分,应通过主键约束和唯一约束来声明;唯一索引则更多地作为查询优化手段使用。约束保证数据正确,索引服务查询性能,按需选择即可。