集合操作

PL/SQL中处理关联数组或嵌套表等集合类型时,您需要查询元素、增删改查或进行集合间的比较与运算。为了简化这些操作,PolarDB PostgreSQL版(兼容Oracle)提供了一套丰富的内置集合方法和运算符。这些工具使您能够像操作标准数据结构一样,直观、高效地管理集合数据,从而增强代码的可读性和维护性。

功能简介

集合操作是针对PL/SQL集合类型(如关联数组、嵌套表)的一系列内置方法和运算符。它们提供了查询、修改、管理和比较集合的功能,语法简洁直观。用于对两个兼容类型的集合进行比较或运算,例如判断子集关系(SUBMULTISET)或计算并集(MULTISET UNION)。

collection_variable.method_name[(parameters)]

适用范围

您的PolarDB PostgreSQL版(兼容Oracle)集群的修订版本需为2.0.14.17.36.0及以上

说明

您可在控制台查看内核小版本号,也可以通过SHOW polardb_version;语句查看。如未满足内核小版本要求,请升级内核小版本

操作说明

多重集合操作方法

IS_EMPTY

检查一个集合是否为空。

collection IS EMPTY

返回值

BOOLEAN类型

  • TRUE:如果集合为空或未初始化。

  • FALSE:如果集合包含至少一个元素。

特点

  • 功能上等价于collection.COUNT = 0,但性能更优。

  • NULL集合返回TRUE

IS_NOT_EMPTY

检查一个集合是否非空。

collection IS NOT EMPTY

返回值

BOOLEAN类型

  • TRUE:如果集合包含至少一个元素。

  • FALSE:如果集合为空或未初始化。

特点

  • 等价于collection.COUNT > 0

  • IS EMPTY的逻辑反操作。

IS_IN

检查指定的值是否存在于集合中。

element_value IN (collection)

参数说明

element_value:要查找的值。
返回值

BOOLEAN类型

  • TRUE:如果值存在于集合中。

  • FALSE:如果值不存在于集合中。

特点

  • 支持NULL值比较。

  • 对于嵌套表和VARRAY特别有用。

  • 时间复杂度为 O(n)。

IS_NOT_IN

检查指定的值是否不存在于集合中。

element_value NOT IN (collection)

参数说明

element_value:要查找的值。

返回值

BOOLEAN类型

  • TRUE:如果值不存在于集合中。

  • FALSE:如果值存在于集合中。

特点

  • IN运算符的逻辑反操作。

  • 处理NULL值时需要特别注意。

IS_SUBMULTISET

检查第一个集合是否为第二个集合的子多重集合。

collection1 SUBMULTISET collection2

返回值

BOOLEAN类型

  • TRUE:如果collection1collection2的子集。

  • FALSE:如果collection1不是collection2的子集。

特点

  • 判断时会考虑元素的重复次数。例如,{1}{1, 2}的子集,但{1, 1}不是。

  • 空集合是任何集合的子集。

  • 任何集合都是其自身的子集。

IS_NOT_SUBMULTISET

检查第一个集合是否不是第二个集合的子多重集合。

collection1 NOT SUBMULTISET collection2

返回值

BOOLEAN类型

  • TRUE:如果collection1不是collection2的子集。

  • FALSE:如果collection1collection2的子集。

IS_MEMBER

检查指定元素是否存在于集合中。

element_value IN (collection)

参数说明

element_value:要检查的元素值。

返回值

BOOLEAN类型

  • TRUE:如果元素是集合的成员。

  • FALSE:如果元素不是集合的成员。

特点

  • 功能类似于IS_IN运算符,但语法不同。

  • 支持复杂数据类型(如RECORD类型)的比较。

IS_NOT_MEMBER

检查指定元素是否不存在于集合中。

element_value NOT MEMBER collection

参数说明

element_value:要检查的元素值。

返回值

BOOLEAN类型

  • TRUE:如果元素不是集合的成员。

  • FALSE:如果元素是集合的成员。

IS_SET

检查一个集合是否不包含重复元素(即符合数学意义上的集合定义)。

collection IS A SET

返回值

BOOLEAN类型

  • TRUE:如果集合中没有重复元素。

  • FALSE:如果集合中存在重复元素。

特点

  • 对于关联数组,此方法总是返回TRUE,因为它们的键是唯一的。

  • 对于嵌套表需要检查值的唯一性。

IS_NOT_SET

检查一个集合是否包含重复元素。

collection IS NOT A SET

返回值

BOOLEAN类型

  • TRUE:如果集合中存在重复元素。

  • FALSE:如果集合中没有重复元素。

多重集合运算符

MULTISET_UNION

计算两个集合的并集,并返回一个新的集合。

collection1 MULTISET UNION [ALL | DISTINCT] collection2

变体

  • MULTISET UNION:默认行为。返回的结果包含collection1collection2中的所有元素。如果一个元素在两个集合中都存在,它会在结果中出现两次。

  • MULTISET UNION ALL:等同于MULTISET UNION

  • MULTISET UNION DISTINCT:返回的结果包含collection1collection2中的所有元素,但会去除重复项,每个元素只出现一次。

返回值

一个新的集合,包含两个集合的所有元素,且其元素类型与输入集合相同。

MULTISET_INTERSECT

计算两个集合的交集,并返回一个新的集合。

collection1 MULTISET INTERSECT [ALL | DISTINCT] collection2

变体

  • MULTISET INTERSECT:默认行为。返回两个集合共有的元素。如果一个元素在collection1中出现n次,在collection2中出现m次,那么它在结果中将出现MIN(n, m)次。

  • MULTISET INTERSECT ALL:等同于MULTISET INTERSECT

  • MULTISET INTERSECT DISTINCT:返回两个集合共有的元素,但会去除重复项,每个元素只出现一次。

返回值

一个新的集合,包含两个集合的共同元素。

MULTISET_EXCEPT

计算两个集合的差集(属于collection1但不属于collection2的元素),并返回一个新的集合。

collection1 MULTISET EXCEPT [ALL | DISTINCT] collection2

变体

  • MULTISET EXCEPT:默认行为。从collection1中移除在collection2中也存在的元素。如果一个元素在collection1中出现n次,在collection2中出现m次,那么它在结果中将出现MAX(n - m, 0)次。

  • MULTISET EXCEPT ALL:等同于MULTISET EXCEPT

  • MULTISET EXCEPT DISTINCT:在计算差集前,先对collection1collection2进行去重,然后返回仅存在于去重后collection1中的元素。

返回值

一个新的集合,包含第一个集合中不在第二个集合中的元素。

使用示例

以下示例展示了集合方法和运算符的实际应用。

示例1:使用集合方法管理关联数组

本示例演示了如何使用COUNTFIRSTLASTEXISTSDELETE等方法来管理一个关联数组。

DECLARE
    TYPE aarray_type IS TABLE OF INT INDEX BY VARCHAR(10);
    aarray aarray_type;
BEGIN
    -- 初始化数组
    aarray('apple')  := 10;
    aarray('banana') := 20;
    aarray('cherry') := 30;

    -- 1. 查询集合状态
    RAISE NOTICE '元素数量 (COUNT): %', aarray.COUNT;
    RAISE NOTICE '第一个键 (FIRST): %', aarray.FIRST;
    RAISE NOTICE '最后一个键 (LAST): %', aarray.LAST;

    -- 2. 检查元素是否存在
    RAISE NOTICE '键 "banana" 是否存在 (EXISTS): %', aarray.EXISTS('banana');
    RAISE NOTICE '键 "date" 是否存在 (EXISTS): %', aarray.EXISTS('date');
    
    -- 3. 删除元素
    RAISE NOTICE '删除前: %', aarray;
    aarray.DELETE('banana');
    RAISE NOTICE '删除 "banana" 后: %', aarray;

    -- 4. 清空集合
    aarray.DELETE;
    RAISE NOTICE '全部删除后,元素数量: %', aarray.COUNT;
END;
/

预期输出:

NOTICE: 元素数量 (COUNT): 3
NOTICE: 第一个键 (FIRST): apple
NOTICE: 最后一个键 (LAST): cherry
NOTICE: 键 "banana" 是否存在 (EXISTS): t
NOTICE: 键 "date" 是否存在 (EXISTS): f
NOTICE: 删除前: (apple=>10,banana=>20,cherry=>30)
NOTICE: 删除 "banana" 后: (apple=>10,cherry=>30)
NOTICE: 全部删除后,元素数量: 0

示例2:使用NEXTPRIOR遍历集合

本示例展示了如何使用FIRSTNEXT组合来安全地遍历关联数组的所有元素。

DECLARE
    TYPE aarray_type IS TABLE OF INT INDEX BY VARCHAR(10);
    aarray aarray_type;
    v_key VARCHAR(10);
BEGIN
    aarray('alpha') := 1;
    aarray('beta')  := 2;
    aarray('gamma') := 3;

    -- 使用循环遍历
    v_key := aarray.FIRST;
    WHILE v_key IS NOT NULL LOOP
        RAISE NOTICE '键: %, 值: %', v_key, aarray(v_key);
        v_key := aarray.NEXT(v_key);
    END LOOP;
    
    -- 查询前一个键
    RAISE NOTICE '键 "gamma" 的前一个键 (PRIOR): %', aarray.PRIOR('gamma');
END;
/

预期输出(注意:关联数组内部会按键排序):

NOTICE: 键: alpha, 值: 1
NOTICE: 键: beta, 值: 2
NOTICE: 键: gamma, 值: 3
NOTICE: 键 "gamma" 的前一个键 (PRIOR): beta

示例3:使用多重集合运算符

本示例演示了如何使用MULTISET UNIONMULTISET INTERSECTMULTISET EXCEPT对两个嵌套表进行集合运算。

DECLARE
    TYPE ntable_type IS TABLE OF INT;
    ntable1 ntable_type := ntable_type(1, 2, 2, 3);
    ntable2 ntable_type := ntable_type(2, 3, 4, 4);
    result_table ntable_type;
BEGIN
    -- 1. 并集 (UNION)
    result_table := ntable1 MULTISET UNION ntable2;
    RAISE NOTICE '并集 (UNION): %', result_table;

    -- 2. 交集 (INTERSECT)
    result_table := ntable1 MULTISET INTERSECT ntable2;
    RAISE NOTICE '交集 (INTERSECT): %', result_table;

    -- 3. 差集 (EXCEPT)
    result_table := ntable1 MULTISET EXCEPT ntable2;
    RAISE NOTICE '差集 (EXCEPT): %', result_table;
END;
/

预期输出:

NOTICE: 并集 (UNION): {1,2,2,3,2,3,4,4}
NOTICE: 交集 (INTERSECT): {2,3}
NOTICE: 差集 (EXCEPT): {1,2}

示例4:使用EXTEND管理嵌套表

本示例展示了如何使用EXTEND方法向一个嵌套表中添加新元素。

DECLARE
    TYPE ntable_type IS TABLE OF INT;
    ntable ntable_type := ntable_type(10, 20);
BEGIN
    RAISE NOTICE '原始嵌套表: %', ntable;

    -- 扩展2个NULL元素
    ntable.EXTEND(2); 
    RAISE NOTICE '扩展2个元素后: %', ntable;

    -- 为新元素赋值
    ntable(3) := 30;
    ntable(4) := 40;
    RAISE NOTICE '赋值后: %', ntable;
END;
/

预期输出:

NOTICE: 原始嵌套表: {10,20}
NOTICE: 扩展2个元素后: {10,20,NULL,NULL}
NOTICE: 赋值后: {10,20,30,40}