Skip to content

Commit b28a372

Browse files
authored
Merge pull request #240 from OreoYang/feat/global_unique_index
docs(v1.17): add Global Unique Index documentation (CN & EN)
2 parents 6a23486 + 0715eac commit b28a372

File tree

4 files changed

+214
-0
lines changed

4 files changed

+214
-0
lines changed

CN/modules/ROOT/nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
** xref:v1.17/17.adoc[7、兼容Oracle函数与存储过程]
3030
** xref:v1.17/18.adoc[8、内置数据类型与内置函数]
3131
** xref:v1.17/19.adoc[9、新增Oracle兼容模式的端口与IP]
32+
** xref:v1.17/41.adoc[10、全局唯一索引]
3233
* xref:v1.17/20.adoc[社区贡献指南]
3334
* xref:v1.17/21.adoc[工具参考]
3435
* xref:v1.17/22.adoc[FAQ]
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
:sectnums:
2+
:sectnumlevels: 5
3+
4+
:imagesdir: ./_images
5+
6+
= 全局唯一索引
7+
8+
== 功能介绍
9+
10+
全局唯一索引(Global Unique Index)是 IvorySQL 针对分区表提供的跨分区唯一性约束能力。
11+
12+
标准 PostgreSQL 的唯一索引仅在单个分区内强制执行唯一性,无法保证跨分区的数据唯一。IvorySQL 引入 `GLOBAL` 关键字,在创建唯一索引时指定该关键字后,数据库在执行插入或更新操作时会扫描所有分区,确保整张分区表范围内不存在重复数据。
13+
14+
该功能同时支持 PG 兼容模式与 Oracle 兼容模式。
15+
16+
== 语法
17+
18+
[source,sql]
19+
----
20+
CREATE UNIQUE INDEX [ index_name ] ON partitioned_table ( column [, ...] ) GLOBAL;
21+
----
22+
23+
参数说明:
24+
25+
- `UNIQUE`:必须与 `GLOBAL` 同时使用,指定唯一性约束;
26+
- `GLOBAL`:启用跨分区唯一性检查,仅对分区表有效;
27+
- `index_name`:可选,省略时数据库自动生成索引名。
28+
29+
== 测试用例
30+
31+
=== 创建分区表与全局唯一索引
32+
33+
[source,sql]
34+
----
35+
-- 创建分区表
36+
CREATE TABLE gidxpart (a int, b int, c text) PARTITION BY RANGE (a);
37+
CREATE TABLE gidxpart1 PARTITION OF gidxpart FOR VALUES FROM (1) TO (10);
38+
CREATE TABLE gidxpart2 PARTITION OF gidxpart FOR VALUES FROM (10) TO (100);
39+
CREATE TABLE gidxpart3 PARTITION OF gidxpart FOR VALUES FROM (100) TO (200);
40+
41+
-- 在分区表上创建全局唯一索引(指定索引名)
42+
CREATE UNIQUE INDEX gidx_u ON gidxpart USING btree(b) GLOBAL;
43+
44+
-- 在分区表上创建全局唯一索引(不指定索引名)
45+
CREATE UNIQUE INDEX ON gidxpart (b) GLOBAL;
46+
----
47+
48+
=== 插入数据:跨分区唯一性验证
49+
50+
[source,sql]
51+
----
52+
-- 以下插入成功(各分区间 b 列无重复)
53+
INSERT INTO gidxpart VALUES (1, 1, 'first');
54+
INSERT INTO gidxpart VALUES (11, 11, 'eleventh');
55+
INSERT INTO gidxpart VALUES (2, 120, 'second');
56+
INSERT INTO gidxpart VALUES (12, 2, 'twelfth');
57+
INSERT INTO gidxpart VALUES (150, 13, 'no duplicate b');
58+
59+
-- 以下插入失败:b=11 已存在于其他分区,违反跨分区唯一性约束
60+
INSERT INTO gidxpart VALUES (2, 11, 'duplicated (b)=(11) on other partition');
61+
-- ERROR: duplicate key value violates unique constraint
62+
63+
INSERT INTO gidxpart VALUES (12, 1, 'duplicated (b)=(1) on other partition');
64+
-- ERROR: duplicate key value violates unique constraint
65+
66+
INSERT INTO gidxpart VALUES (150, 11, 'duplicated (b)=(11) on other partition');
67+
-- ERROR: duplicate key value violates unique constraint
68+
----
69+
70+
=== 更新数据:跨分区唯一性验证
71+
72+
[source,sql]
73+
----
74+
-- 更新操作同样受全局唯一索引约束
75+
UPDATE gidxpart SET b = 2 WHERE a = 2;
76+
-- ERROR: duplicate key value violates unique constraint(b=2 已存在)
77+
78+
UPDATE gidxpart SET b = 12 WHERE a = 12;
79+
-- 成功(b=12 在全局范围内唯一)
80+
----
81+
82+
=== 分区的 ATTACH 与 DETACH
83+
84+
[source,sql]
85+
----
86+
-- 创建一个独立的表,用于后续 ATTACH
87+
CREATE TABLE gidxpart_new (a int, b int, c text);
88+
INSERT INTO gidxpart_new VALUES (100001, 11, 'conflict with gidxpart1');
89+
90+
-- ATTACH 时若存在跨分区重复值,操作将失败
91+
ALTER TABLE gidxpart ATTACH PARTITION gidxpart_new
92+
FOR VALUES FROM (100000) TO (199999);
93+
-- ERROR: duplicate key value violates unique constraint
94+
95+
-- DETACH 分区后,该分区的全局索引类型恢复为普通索引
96+
ALTER TABLE gidxpart DETACH PARTITION gidxpart2;
97+
----
98+
99+
== 功能限制
100+
101+
. `GLOBAL` 关键字必须与 `UNIQUE` 配合使用,不支持创建全局非唯一索引;
102+
. 全局唯一索引仅适用于**分区表**,普通表不支持该关键字;
103+
. 每次插入或更新操作均需扫描所有分区以验证唯一性,在分区数量较多或数据量较大时存在**性能开销**;
104+
. 通过 `ATTACH PARTITION` 挂载分区时,若新分区中存在与其他分区重复的数据,挂载操作将失败;
105+
. 分区被 `DETACH` 后,其对应的全局索引自动降级为普通局部索引;
106+
. 不支持在子分区(二级分区)上单独创建全局唯一索引,全局唯一性约束由顶层分区表统一管理。

EN/modules/ROOT/nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
** xref:v1.17/17.adoc[7、Compatible with Oracle functions and stored procedures]
3030
** xref:v1.17/18.adoc[8、Built-in data types and built-in functions]
3131
** xref:v1.17/19.adoc[9、Added Oracle compatibility mode ports and IP]
32+
** xref:v1.17/41.adoc[10、Global Unique Index]
3233
* xref:v1.17/20.adoc[Community contribution]
3334
* xref:v1.17/21.adoc[Tool Reference]
3435
* xref:v1.17/22.adoc[FAQ]
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
:sectnums:
2+
:sectnumlevels: 5
3+
4+
:imagesdir: ./_images
5+
6+
= Global Unique Index
7+
8+
== Overview
9+
10+
The Global Unique Index is a cross-partition uniqueness constraint feature provided by IvorySQL for partitioned tables.
11+
12+
Standard PostgreSQL unique indexes only enforce uniqueness within a single partition and cannot guarantee uniqueness across partitions. IvorySQL introduces the `GLOBAL` keyword: when specified during unique index creation, the database scans all partitions on every INSERT or UPDATE to ensure no duplicate values exist across the entire partitioned table.
13+
14+
This feature is supported in both PG-compatible mode and Oracle-compatible mode.
15+
16+
== Syntax
17+
18+
[source,sql]
19+
----
20+
CREATE UNIQUE INDEX [ index_name ] ON partitioned_table ( column [, ...] ) GLOBAL;
21+
----
22+
23+
Parameter description:
24+
25+
- `UNIQUE`: must be used together with `GLOBAL` to specify the uniqueness constraint;
26+
- `GLOBAL`: enables cross-partition uniqueness checking; only valid on partitioned tables;
27+
- `index_name`: optional; if omitted, the database generates an index name automatically.
28+
29+
== Test Cases
30+
31+
=== Create a Partitioned Table and Global Unique Index
32+
33+
[source,sql]
34+
----
35+
-- Create a partitioned table
36+
CREATE TABLE gidxpart (a int, b int, c text) PARTITION BY RANGE (a);
37+
CREATE TABLE gidxpart1 PARTITION OF gidxpart FOR VALUES FROM (1) TO (10);
38+
CREATE TABLE gidxpart2 PARTITION OF gidxpart FOR VALUES FROM (10) TO (100);
39+
CREATE TABLE gidxpart3 PARTITION OF gidxpart FOR VALUES FROM (100) TO (200);
40+
41+
-- Create a global unique index with an explicit name
42+
CREATE UNIQUE INDEX gidx_u ON gidxpart USING btree(b) GLOBAL;
43+
44+
-- Create a global unique index without specifying a name
45+
CREATE UNIQUE INDEX ON gidxpart (b) GLOBAL;
46+
----
47+
48+
=== INSERT: Cross-Partition Uniqueness Validation
49+
50+
[source,sql]
51+
----
52+
-- These inserts succeed (no duplicate values in column b across partitions)
53+
INSERT INTO gidxpart VALUES (1, 1, 'first');
54+
INSERT INTO gidxpart VALUES (11, 11, 'eleventh');
55+
INSERT INTO gidxpart VALUES (2, 120, 'second');
56+
INSERT INTO gidxpart VALUES (12, 2, 'twelfth');
57+
INSERT INTO gidxpart VALUES (150, 13, 'no duplicate b');
58+
59+
-- These inserts fail: b=11 already exists in another partition
60+
INSERT INTO gidxpart VALUES (2, 11, 'duplicated (b)=(11) on other partition');
61+
-- ERROR: duplicate key value violates unique constraint
62+
63+
INSERT INTO gidxpart VALUES (12, 1, 'duplicated (b)=(1) on other partition');
64+
-- ERROR: duplicate key value violates unique constraint
65+
66+
INSERT INTO gidxpart VALUES (150, 11, 'duplicated (b)=(11) on other partition');
67+
-- ERROR: duplicate key value violates unique constraint
68+
----
69+
70+
=== UPDATE: Cross-Partition Uniqueness Validation
71+
72+
[source,sql]
73+
----
74+
-- UPDATE operations are also subject to the global unique index
75+
UPDATE gidxpart SET b = 2 WHERE a = 2;
76+
-- ERROR: duplicate key value violates unique constraint (b=2 already exists)
77+
78+
UPDATE gidxpart SET b = 12 WHERE a = 12;
79+
-- Succeeds (b=12 is unique across all partitions)
80+
----
81+
82+
=== Partition ATTACH and DETACH
83+
84+
[source,sql]
85+
----
86+
-- Create a standalone table for ATTACH testing
87+
CREATE TABLE gidxpart_new (a int, b int, c text);
88+
INSERT INTO gidxpart_new VALUES (100001, 11, 'conflict with gidxpart1');
89+
90+
-- ATTACH fails if the new partition contains values that duplicate existing ones
91+
ALTER TABLE gidxpart ATTACH PARTITION gidxpart_new
92+
FOR VALUES FROM (100000) TO (199999);
93+
-- ERROR: duplicate key value violates unique constraint
94+
95+
-- DETACH is allowed; the partition's global index reverts to a regular local index
96+
ALTER TABLE gidxpart DETACH PARTITION gidxpart2;
97+
----
98+
99+
== Limitations
100+
101+
. The `GLOBAL` keyword must be used together with `UNIQUE`; global non-unique indexes are not supported;
102+
. Global unique indexes are only applicable to **partitioned tables**; the keyword is not valid on regular tables;
103+
. Every INSERT or UPDATE requires scanning all partitions to verify uniqueness, which introduces **performance overhead** when the number of partitions or data volume is large;
104+
. When attaching a partition via `ATTACH PARTITION`, the operation will fail if the new partition contains data that duplicates values in existing partitions;
105+
. After a partition is detached with `DETACH PARTITION`, the corresponding global index automatically reverts to a regular local (partition-level) index;
106+
. Creating a global unique index independently on a sub-partition (second-level partition) is not supported; cross-partition uniqueness is managed exclusively at the top-level partitioned table.

0 commit comments

Comments
 (0)