关联数组是一种将唯一键与值相关联的集合。键不必是数字,也可以是字符数据。

关联数组具有以下特征:

  • 必须定义关联数组类型,然后才能声明该数组类型的数组变量。使用数组变量进行数据操作。
  • 声明数组变量时,会创建关联数组,但它是空的 - 只需开始为键值赋值即可。
  • 如果指定了INDEXBY BINARY_INTEGER或PLS_INTEGER,则键可以是任何负整数、正整数或零。
  • 如果指定了INDEXBY VARCHAR2,则键可以是字符数据。
  • 数组中的元素数量没有预定义的限制 - 它会随着添加的元素而动态增长。
  • 数组可能是稀疏的 - 在键值的赋值中可能存在间隙。
  • 尝试引用尚未赋值的数组元素将导致异常。

TYPE IS TABLE OF ... INDEX BY语句用于定义关联数组类型。

TYPE assoctype IS TABLE OF { datatype | rectype | objtype }
  INDEX BY { BINARY_INTEGER | PLS_INTEGER | VARCHAR2(n) };

assoctype是分配给数组类型的标识符。datatype是标量数据类型,例如VARCHAR2或NUMBER。rectype是先前定义的记录类型。objtype 是先前定义的对象类型。n是字符键的最大长度。

为了使用该数组,必须声明一个该数组类型的变量。以下是声明数组变量的语法。

array assoctype

array是分配给关联数组的标识符。assoctype是先前定义的数组类型的标识符。

使用以下语法引用数组的元素。

array(n)[.field ]

array是先前声明的数组的标识符。n是键值,其类型与INDEX BY子句中给出的数据类型兼容。如果从记录类型或对象类型定义array的数组类型,则 [.field ] 必须引用定义数组类型时所依据的对象类型中的记录类型或属性内的单个字段。或者,可以通过省略 [.field ] 来引用整个记录。

以下示例从emp表中读取前十名员工的姓名,将它们存储在一个数组中,然后显示数组中的结果。

DECLARE
    TYPE emp_arr_typ IS TABLE OF VARCHAR2(10) INDEX BY BINARY_INTEGER;
    emp_arr         emp_arr_typ;
    CURSOR emp_cur IS SELECT ename FROM emp WHERE ROWNUM <= 10;
    i               INTEGER := 0;
BEGIN
    FOR r_emp IN emp_cur LOOP
        i := i + 1;
        emp_arr(i) := r_emp.ename;
    END LOOP;
    FOR j IN 1..10 LOOP
        DBMS_OUTPUT.PUT_LINE(emp_arr(j));
    END LOOP;
END;

上面的示例生成以下输出:

SMITH
ALLEN
WARD
JONES
MARTIN
BLAKE
CLARK
SCOTT
KING
TURNER

现在修改上一个示例以使用数组定义中的记录类型。

DECLARE
    TYPE emp_rec_typ IS RECORD (
        empno       NUMBER(4),
        ename       VARCHAR2(10)
    );
    TYPE emp_arr_typ IS TABLE OF emp_rec_typ INDEX BY BINARY_INTEGER;
    emp_arr         emp_arr_typ;
    CURSOR emp_cur IS SELECT empno, ename FROM emp WHERE ROWNUM <= 10;
    i               INTEGER := 0;
BEGIN
    DBMS_OUTPUT.PUT_LINE('EMPNO    ENAME');
    DBMS_OUTPUT.PUT_LINE('-----    -------');
    FOR r_emp IN emp_cur LOOP
        i := i + 1;
        emp_arr(i).empno := r_emp.empno;
        emp_arr(i).ename := r_emp.ename;
    END LOOP;
    FOR j IN 1..10 LOOP
        DBMS_OUTPUT.PUT_LINE(emp_arr(j).empno || '     ' ||
            emp_arr(j).ename);
    END LOOP;
END;

下面是此匿名块的输出。

EMPNO    ENAME
-----    -------
7369     SMITH
7499     ALLEN
7521     WARD
7566     JONES
7654     MARTIN
7698     BLAKE
7782     CLARK
7788     SCOTT
7839     KING
7844     TURNER

emp%ROWTYPE属性可用于定义emp_arr_typ,而不是使用emp_rec_typ记录类型,如下所示。

DECLARE
    TYPE emp_arr_typ IS TABLE OF emp%ROWTYPE INDEX BY BINARY_INTEGER;
    emp_arr         emp_arr_typ;
    CURSOR emp_cur IS SELECT empno, ename FROM emp WHERE ROWNUM <= 10;
    i               INTEGER := 0;
BEGIN
    DBMS_OUTPUT.PUT_LINE('EMPNO    ENAME');
    DBMS_OUTPUT.PUT_LINE('-----    -------');
    FOR r_emp IN emp_cur LOOP
        i := i + 1;
        emp_arr(i).empno := r_emp.empno;
        emp_arr(i).ename := r_emp.ename;
    END LOOP;
    FOR j IN 1..10 LOOP
        DBMS_OUTPUT.PUT_LINE(emp_arr(j).empno || '     ' ||
            emp_arr(j).ename);
    END LOOP;
END;

结果与前面的例子相同。

不是单独分配记录的每个字段,而是可以从r_emp到emp_arr进行记录级别分配。

DECLARE
    TYPE emp_rec_typ IS RECORD (
        empno       NUMBER(4),
        ename       VARCHAR2(10)
    );
    TYPE emp_arr_typ IS TABLE OF emp_rec_typ INDEX BY BINARY_INTEGER;
    emp_arr         emp_arr_typ;
    CURSOR emp_cur IS SELECT empno, ename FROM emp WHERE ROWNUM <= 10;
    i               INTEGER := 0;
BEGIN
    DBMS_OUTPUT.PUT_LINE('EMPNO    ENAME');
    DBMS_OUTPUT.PUT_LINE('-----    -------');
    FOR r_emp IN emp_cur LOOP
        i := i + 1;
        emp_arr(i) := r_emp;
    END LOOP;
    FOR j IN 1..10 LOOP
        DBMS_OUTPUT.PUT_LINE(emp_arr(j).empno || '     ' ||
            emp_arr(j).ename);
    END LOOP;
END;

关联数组的键可以是字符数据,如以下示例所示。

DECLARE
    TYPE job_arr_typ IS TABLE OF NUMBER INDEX BY VARCHAR2(9);
    job_arr         job_arr_typ;
BEGIN
    job_arr('ANALYST')   := 100;
    job_arr('CLERK')     := 200;
    job_arr('MANAGER')   := 300;
    job_arr('SALESMAN')  := 400;
    job_arr('PRESIDENT') := 500;
    DBMS_OUTPUT.PUT_LINE('ANALYST  : ' || job_arr('ANALYST'));
    DBMS_OUTPUT.PUT_LINE('CLERK    : ' || job_arr('CLERK'));
    DBMS_OUTPUT.PUT_LINE('MANAGER  : ' || job_arr('MANAGER'));
    DBMS_OUTPUT.PUT_LINE('SALESMAN : ' || job_arr('SALESMAN'));
    DBMS_OUTPUT.PUT_LINE('PRESIDENT: ' || job_arr('PRESIDENT'));
END;

ANALYST  : 100
CLERK    : 200
MANAGER  : 300
SALESMAN : 400
PRESIDENT: 500

二维关联数组

二维关联数组是嵌套的关联数组,二维关联数组的值是一维关联数组,可以同时使用两个键关联到最内层关联数组的元素。二维关联数组的基本特征和关联数组相同。

  • 二维关联数组支持的场景
    在只有一维关联数组的情况下,如果需要实现类似二维关联数组的功能,需要使用一个中转表间接实现,示例如下:
    DECLARE
        type TB1 is table of varchar(10) index by varchar(10);
        type TB2 is table of TB1 index by varchar(10);
        v_table1 TB1;
        v_table2 TB2;
    BEGIN
        v_table1('a') := 1;
        v_table1('b') := 2;
        v_table1('c') := 3;
        v_table2('a') := v_table1;
    END;
    在支持了二维关联数组后,可以不用定义中转数组v_table1,直接使用二维下标,对v_table2完成赋值操作:
    DECLARE
        type TB1 is table of varchar(10) index by varchar(10);
        type TB2 is table of TB1 index by varchar(10);
        v_table2 TB2;
    BEGIN
        v_table2('a')('a') := 1;
        v_table2('a')('b') := 2;
        v_table2('a')('c') := 3;
    END;
  • 示例
    使用二维关联数组示例如下所示:
    -- Examples of dim-2 associative array 
    DECLARE
        type type_row is table of varchar(10) index by varchar(10);
        type type_table is table of type_row index by varchar(10);
        v_table type_table;
        i varchar2(64);
        i_2 varchar2(64);
    BEGIN
        v_table('a')('b') := 10;
        v_table('a')('c') := 11;
        v_table('z')('b') := 12;
        v_table('z')('c') := 13;
        i := v_table.FIRST;
        WHILE i IS NOT NULL LOOP
            i_2 := v_table(i).FIRST;
            WHILE i_2 IS NOT NULL LOOP
                dbms_output.put_line(i || ' ' || i_2 || '-' || TO_CHAR(v_table(i)(i_2)));
                i_2 := v_table(i).NEXT(i_2);
            END LOOP;
            i := v_table.NEXT(i);
        END LOOP;
    END;
    以下是此匿名块的输出:
    a b-10
    a c-11
    z b-12
    z c-13
  • 集合方法
    目前二维关联数组支持的集合方法有:COUNT,FIRST,LAST,NEXT,PRIOR,EXISTS。
    -- 对于无参数的集合方法,比如COUNT,采用如下形式的方法进行调用:
    array_dim2(n)
    -- 对于含有一个参数的集合方法,比如NEXT,采用如下形式的方法进行调用:
    array_dim2(n).op(n)
    说明
    • array_dim2:为先前声明的二维关联数组的标识符。
    • n:为键值,其类型与INDEX BY子句中给出的数据类型兼容。
    • op:为集合方法。
    以下为一个二维关联数组使用集合方法的示例:
    DECLARE
        type TB1 is table of varchar(10) index by varchar(10);
        type TB2 is table of TB1 index by varchar(10);
        type TB3 is table of TB2 index by varchar(10);
        v_table TB2;
        v_table3 TB3;
    BEGIN
        v_table('a')('b') := 10;
        v_table('b')('c') := 11;
        v_table('c')('b') := 12;
        v_table('d')('c') := 13;
        v_table3('a') := v_table;
        
        dbms_output.put_line(v_table3('a').COUNT); 
        dbms_output.put_line(v_table3('a').FIRST);
        dbms_output.put_line(v_table3('a').LAST); 
        dbms_output.put_line(v_table3('a').NEXT(v_table3('a').FIRST));
        dbms_output.put_line(v_table3('a').prior(v_table3('a').LAST));
        dbms_output.put_line(v_table3('a').exists(v_table3('a').FIRST));
    END;
    以下是此匿名块的输出:
    4
    a
    d
    b
    c
    t
  • 暂不支持的场景

    目前只支持二维的关联数组,嵌套表和varray目前还不支持二维下标操作。

    目前二维关联数组还不支持记录类型(record类型):
    DECLARE
        TYPE emp_typ IS RECORD (
            ename       varchar(10),
            first         varchar(10)
        );
        type TB is table of emp_typ index by varchar(10);
        type TB2 is table of TB index by varchar(10);
        mytable     TB2;
        myrecord    emp_typ;
    BEGIN
        mytable('a')('b') := myrecord;
    END;
    以下是此匿名块的输出:
    ERROR:  At present, only associative array without package and IS TABLE OF type is not record type support multidimensional subscripts search and assignment
    CONTEXT:  compilation of SPL function "inline_code_block" near line 11
    嵌套表和varray不能包含一个二维关联数组,示例如下:
    DECLARE
        type TB1 is table of number index by number;
        type TB2 is table of TB1 index by number;
        type TB3 is table of TB2;
        v_table2 TB2;
        v_table3 TB3;
    BEGIN
        v_table2(1)(1) := 1;
        v_table2(1)(2) := 2;
        v_table2(2)(3) := 3;
        v_table2(2)(4) := 4;
    
        v_table3 := TB3(v_table2);
    END;
    以下是此匿名块的输出:
    ERROR:  Nested table and Varray can't be assgined with a multidimensional associative array