diff --git a/CN/modules/ROOT/nav.adoc b/CN/modules/ROOT/nav.adoc index 13890a93..9cbb16ef 100644 --- a/CN/modules/ROOT/nav.adoc +++ b/CN/modules/ROOT/nav.adoc @@ -29,6 +29,7 @@ ** xref:v1.17/17.adoc[7、兼容Oracle函数与存储过程] ** xref:v1.17/18.adoc[8、内置数据类型与内置函数] ** xref:v1.17/19.adoc[9、新增Oracle兼容模式的端口与IP] +** xref:v1.17/41.adoc[10、全局唯一索引] * xref:v1.17/20.adoc[社区贡献指南] * xref:v1.17/21.adoc[工具参考] * xref:v1.17/22.adoc[FAQ] diff --git a/CN/modules/ROOT/pages/v1.17/41.adoc b/CN/modules/ROOT/pages/v1.17/41.adoc new file mode 100644 index 00000000..b7077687 --- /dev/null +++ b/CN/modules/ROOT/pages/v1.17/41.adoc @@ -0,0 +1,106 @@ +:sectnums: +:sectnumlevels: 5 + +:imagesdir: ./_images + += 全局唯一索引 + +== 功能介绍 + +全局唯一索引(Global Unique Index)是 IvorySQL 针对分区表提供的跨分区唯一性约束能力。 + +标准 PostgreSQL 的唯一索引仅在单个分区内强制执行唯一性,无法保证跨分区的数据唯一。IvorySQL 引入 `GLOBAL` 关键字,在创建唯一索引时指定该关键字后,数据库在执行插入或更新操作时会扫描所有分区,确保整张分区表范围内不存在重复数据。 + +该功能同时支持 PG 兼容模式与 Oracle 兼容模式。 + +== 语法 + +[source,sql] +---- +CREATE UNIQUE INDEX [ index_name ] ON partitioned_table ( column [, ...] ) GLOBAL; +---- + +参数说明: + +- `UNIQUE`:必须与 `GLOBAL` 同时使用,指定唯一性约束; +- `GLOBAL`:启用跨分区唯一性检查,仅对分区表有效; +- `index_name`:可选,省略时数据库自动生成索引名。 + +== 测试用例 + +=== 创建分区表与全局唯一索引 + +[source,sql] +---- +-- 创建分区表 +CREATE TABLE gidxpart (a int, b int, c text) PARTITION BY RANGE (a); +CREATE TABLE gidxpart1 PARTITION OF gidxpart FOR VALUES FROM (1) TO (10); +CREATE TABLE gidxpart2 PARTITION OF gidxpart FOR VALUES FROM (10) TO (100); +CREATE TABLE gidxpart3 PARTITION OF gidxpart FOR VALUES FROM (100) TO (200); + +-- 在分区表上创建全局唯一索引(指定索引名) +CREATE UNIQUE INDEX gidx_u ON gidxpart USING btree(b) GLOBAL; + +-- 在分区表上创建全局唯一索引(不指定索引名) +CREATE UNIQUE INDEX ON gidxpart (b) GLOBAL; +---- + +=== 插入数据:跨分区唯一性验证 + +[source,sql] +---- +-- 以下插入成功(各分区间 b 列无重复) +INSERT INTO gidxpart VALUES (1, 1, 'first'); +INSERT INTO gidxpart VALUES (11, 11, 'eleventh'); +INSERT INTO gidxpart VALUES (2, 120, 'second'); +INSERT INTO gidxpart VALUES (12, 2, 'twelfth'); +INSERT INTO gidxpart VALUES (150, 13, 'no duplicate b'); + +-- 以下插入失败:b=11 已存在于其他分区,违反跨分区唯一性约束 +INSERT INTO gidxpart VALUES (2, 11, 'duplicated (b)=(11) on other partition'); +-- ERROR: duplicate key value violates unique constraint + +INSERT INTO gidxpart VALUES (12, 1, 'duplicated (b)=(1) on other partition'); +-- ERROR: duplicate key value violates unique constraint + +INSERT INTO gidxpart VALUES (150, 11, 'duplicated (b)=(11) on other partition'); +-- ERROR: duplicate key value violates unique constraint +---- + +=== 更新数据:跨分区唯一性验证 + +[source,sql] +---- +-- 更新操作同样受全局唯一索引约束 +UPDATE gidxpart SET b = 2 WHERE a = 2; +-- ERROR: duplicate key value violates unique constraint(b=2 已存在) + +UPDATE gidxpart SET b = 12 WHERE a = 12; +-- 成功(b=12 在全局范围内唯一) +---- + +=== 分区的 ATTACH 与 DETACH + +[source,sql] +---- +-- 创建一个独立的表,用于后续 ATTACH +CREATE TABLE gidxpart_new (a int, b int, c text); +INSERT INTO gidxpart_new VALUES (100001, 11, 'conflict with gidxpart1'); + +-- ATTACH 时若存在跨分区重复值,操作将失败 +ALTER TABLE gidxpart ATTACH PARTITION gidxpart_new + FOR VALUES FROM (100000) TO (199999); +-- ERROR: duplicate key value violates unique constraint + +-- DETACH 分区后,该分区的全局索引类型恢复为普通索引 +ALTER TABLE gidxpart DETACH PARTITION gidxpart2; +---- + +== 功能限制 + +. `GLOBAL` 关键字必须与 `UNIQUE` 配合使用,不支持创建全局非唯一索引; +. 全局唯一索引仅适用于**分区表**,普通表不支持该关键字; +. 每次插入或更新操作均需扫描所有分区以验证唯一性,在分区数量较多或数据量较大时存在**性能开销**; +. 通过 `ATTACH PARTITION` 挂载分区时,若新分区中存在与其他分区重复的数据,挂载操作将失败; +. 分区被 `DETACH` 后,其对应的全局索引自动降级为普通局部索引; +. 不支持在子分区(二级分区)上单独创建全局唯一索引,全局唯一性约束由顶层分区表统一管理。 diff --git a/EN/modules/ROOT/nav.adoc b/EN/modules/ROOT/nav.adoc index 2701d31c..0a6fa954 100644 --- a/EN/modules/ROOT/nav.adoc +++ b/EN/modules/ROOT/nav.adoc @@ -29,6 +29,7 @@ ** xref:v1.17/17.adoc[7、Compatible with Oracle functions and stored procedures] ** xref:v1.17/18.adoc[8、Built-in data types and built-in functions] ** xref:v1.17/19.adoc[9、Added Oracle compatibility mode ports and IP] +** xref:v1.17/41.adoc[10、Global Unique Index] * xref:v1.17/20.adoc[Community contribution] * xref:v1.17/21.adoc[Tool Reference] * xref:v1.17/22.adoc[FAQ] \ No newline at end of file diff --git a/EN/modules/ROOT/pages/v1.17/41.adoc b/EN/modules/ROOT/pages/v1.17/41.adoc new file mode 100644 index 00000000..9c12d2c0 --- /dev/null +++ b/EN/modules/ROOT/pages/v1.17/41.adoc @@ -0,0 +1,106 @@ +:sectnums: +:sectnumlevels: 5 + +:imagesdir: ./_images + += Global Unique Index + +== Overview + +The Global Unique Index is a cross-partition uniqueness constraint feature provided by IvorySQL for partitioned tables. + +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. + +This feature is supported in both PG-compatible mode and Oracle-compatible mode. + +== Syntax + +[source,sql] +---- +CREATE UNIQUE INDEX [ index_name ] ON partitioned_table ( column [, ...] ) GLOBAL; +---- + +Parameter description: + +- `UNIQUE`: must be used together with `GLOBAL` to specify the uniqueness constraint; +- `GLOBAL`: enables cross-partition uniqueness checking; only valid on partitioned tables; +- `index_name`: optional; if omitted, the database generates an index name automatically. + +== Test Cases + +=== Create a Partitioned Table and Global Unique Index + +[source,sql] +---- +-- Create a partitioned table +CREATE TABLE gidxpart (a int, b int, c text) PARTITION BY RANGE (a); +CREATE TABLE gidxpart1 PARTITION OF gidxpart FOR VALUES FROM (1) TO (10); +CREATE TABLE gidxpart2 PARTITION OF gidxpart FOR VALUES FROM (10) TO (100); +CREATE TABLE gidxpart3 PARTITION OF gidxpart FOR VALUES FROM (100) TO (200); + +-- Create a global unique index with an explicit name +CREATE UNIQUE INDEX gidx_u ON gidxpart USING btree(b) GLOBAL; + +-- Create a global unique index without specifying a name +CREATE UNIQUE INDEX ON gidxpart (b) GLOBAL; +---- + +=== INSERT: Cross-Partition Uniqueness Validation + +[source,sql] +---- +-- These inserts succeed (no duplicate values in column b across partitions) +INSERT INTO gidxpart VALUES (1, 1, 'first'); +INSERT INTO gidxpart VALUES (11, 11, 'eleventh'); +INSERT INTO gidxpart VALUES (2, 120, 'second'); +INSERT INTO gidxpart VALUES (12, 2, 'twelfth'); +INSERT INTO gidxpart VALUES (150, 13, 'no duplicate b'); + +-- These inserts fail: b=11 already exists in another partition +INSERT INTO gidxpart VALUES (2, 11, 'duplicated (b)=(11) on other partition'); +-- ERROR: duplicate key value violates unique constraint + +INSERT INTO gidxpart VALUES (12, 1, 'duplicated (b)=(1) on other partition'); +-- ERROR: duplicate key value violates unique constraint + +INSERT INTO gidxpart VALUES (150, 11, 'duplicated (b)=(11) on other partition'); +-- ERROR: duplicate key value violates unique constraint +---- + +=== UPDATE: Cross-Partition Uniqueness Validation + +[source,sql] +---- +-- UPDATE operations are also subject to the global unique index +UPDATE gidxpart SET b = 2 WHERE a = 2; +-- ERROR: duplicate key value violates unique constraint (b=2 already exists) + +UPDATE gidxpart SET b = 12 WHERE a = 12; +-- Succeeds (b=12 is unique across all partitions) +---- + +=== Partition ATTACH and DETACH + +[source,sql] +---- +-- Create a standalone table for ATTACH testing +CREATE TABLE gidxpart_new (a int, b int, c text); +INSERT INTO gidxpart_new VALUES (100001, 11, 'conflict with gidxpart1'); + +-- ATTACH fails if the new partition contains values that duplicate existing ones +ALTER TABLE gidxpart ATTACH PARTITION gidxpart_new + FOR VALUES FROM (100000) TO (199999); +-- ERROR: duplicate key value violates unique constraint + +-- DETACH is allowed; the partition's global index reverts to a regular local index +ALTER TABLE gidxpart DETACH PARTITION gidxpart2; +---- + +== Limitations + +. The `GLOBAL` keyword must be used together with `UNIQUE`; global non-unique indexes are not supported; +. Global unique indexes are only applicable to **partitioned tables**; the keyword is not valid on regular tables; +. 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; +. When attaching a partition via `ATTACH PARTITION`, the operation will fail if the new partition contains data that duplicates values in existing partitions; +. After a partition is detached with `DETACH PARTITION`, the corresponding global index automatically reverts to a regular local (partition-level) index; +. 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.