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

关联数组具有以下特征:

  • 必须定义关联数组类型,然后才能声明该数组类型的数组变量。使用数组变量进行数据操作。
  • 声明数组变量时,会创建关联数组,但它是空的 - 只需开始为键值赋值即可。
  • 如果指定了 INDEX BY BINARY_INTEGER 或 PLS_INTEGER,则键可以是任何负整数、正整数或零。
  • 如果指定了 INDEX BY 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