# 数组
#### 数组类型的使用
在使用数组之前，需要自定义一个数组类型。
在存储过程中紧跟AS关键字后面定义数组类型。定义方法为：
```
TYPE array_type IS VARRAY(size) OF data_type [NOT NULL];
```
其中：
- array_type：要定义的数组类型名。
- VARRAY：表示要定义的数组类型。
- size：取值为正整数，表示可以容纳的成员的最大数量。
- data_type：要创建的数组中成员的类型。
- NOT NULL: 可选约束，可以约束该数组中的元素均不为NULL。
![](https://support.huaweicloud.com/devg-911-dws/public_sys-resources/note_3.0-zh-cn.png)
- 在DWS中，数组会自动增长，访问越界会返回一个NULL，不会报错。越界写入数组会提示：Subscript outside of limit.
- 在存储过程中定义的数组类型，其作用域仅在该存储过程中。
- 建议选择上述定义方法的一种来自定义数组类型，当同时使用两种方法定义同名的数组类型时，DWS会优先选择存储过程中定义的数组类型来声明数组变量。
 
DWS 8.1.0之前版本， 由于数组可以自动增长，系统不会校验数组越界以及数组元素的长度限制。当前版本为了兼容Oracle的用法增加了相关约束。如果已经存在越界写入等场景，可通过在[behavior_compat_options](https://support.huaweicloud.com/devg-911-dws/dws_04_0931.html#ZH-CN_TOPIC_0000002166614152__section1980124735516)参数中配置varray_verification，来兼容之前不校验的行为。
示例：
```
--演示在存储过程中对数组声明操作。
CREATE OR REPLACE PROCEDURE array_proc
AS 
       TYPE ARRAY_INTEGER IS VARRAY(1024) OF INTEGER;--定义数组类型
       TYPE ARRAY_INTEGER_NOT_NULL IS VARRAY(1024) OF INTEGER NOT NULL;--定义非空数组类型
       ARRINT ARRAY_INTEGER := ARRAY_INTEGER();  --声明数组类型的变量 
BEGIN 
       ARRINT.extend(10);  
       FOR I IN 1..10 LOOP  
               ARRINT(I) := I; 
       END LOOP; 
       DBMS_OUTPUT.PUT_LINE(ARRINT.COUNT);  
       DBMS_OUTPUT.PUT_LINE(ARRINT(1));  
       DBMS_OUTPUT.PUT_LINE(ARRINT(10)); 
       DBMS_OUTPUT.PUT_LINE(ARRINT(ARRINT.FIRST)); 
       DBMS_OUTPUT.PUT_LINE(ARRINT(ARRINT.last));
END;  
/
--调用该存储过程。
CALL array_proc();
10
1
10
1
10
--删除存储过程。
DROP PROCEDURE array_proc;
```
#### ROWTYPE类型数组的声明及使用
除了上述示例中普通数组以及not null数组的声明及使用，数组中同时支持rowtype类型数组的声明及使用。
示例：
```
--演示在存储过程中对数组COUNT函数的用法。
CREATE TABLE tbl (a int, b int);
INSERT INTO tbl VALUES(1, 2),(2, 3),(3, 4);
CREATE OR REPLACE PROCEDURE array_proc
AS 
    CURSOR all_tbl IS SELECT * FROM tbl ORDER BY a; 
    TYPE tbl_array_type IS varray(50) OF tbl%rowtype; --定义rowtype类型的数组，tbl是任意表。
    tbl_array tbl_array_type;
    tbl_item tbl%rowtype;
    inx1 int;
BEGIN 
    tbl_array := tbl_array_type();
    inx1 := 0;
    FOR tbl_item IN all_tbl LOOP 
        inx1 := inx1 + 1;  
        tbl_array(inx1) := tbl_item; 
    END LOOP; 
    WHILE inx1 IS NOT NULL LOOP  
        DBMS_OUTPUT.PUT_LINE('tbl_array(inx1).a=' || tbl_array(inx1).a || ' tbl_array(inx1).b=' || tbl_array(inx1).b);  
        inx1 := tbl_array.PRIOR(inx1);
    END LOOP; 
END;
/
```
执行结果：
```
call array_proc();
tbl_array(inx1).a=3 tbl_array(inx1).b=4
tbl_array(inx1).a=2 tbl_array(inx1).b=3
tbl_array(inx1).a=1 tbl_array(inx1).b=2
```
#### 数组相关函数使用
在DWS中，提供了类似Oracle相关的数组函数的支持，可以通过以下函数获取数组的一些属性或者对数组内容进行操作。
#### COUNT
COUNT函数可以返回当前数组元素的数量，仅统计初始化过的元素或者经过EXTEND函数扩展后的元素。
用法如下：
*varray* .COUNT或*varray*.COUNT()
示例：
```
--演示在存储过程中对数组COUNT函数的用法。
CREATE OR REPLACE PROCEDURE test_varray
AS 
    TYPE varray_type IS VARRAY(20) OF INT; 
    v_varray varray_type; 
BEGIN 
    v_varray := varray_type(1, 2, 3);
    DBMS_OUTPUT.PUT_LINE('v_varray.count=' || v_varray.count); 
    v_varray.extend;
    DBMS_OUTPUT.PUT_LINE('v_varray.count=' || v_varray.count); 
END; 
/
```
执行结果：
```
call test_varray();
v_varray.count=3
v_varray.count=4
```
#### FIRST和LAST
FIRST函数可以返回第一个元素的下标。LAST函数可以返回最后一个元素的下标。
用法如下：
*varray* .FIRST或*varray*.FIRST()
*varray* .LAST或*varray*.LAST()
示例：
```
--演示在存储过程中对数组FIRST和LAST函数的用法。
CREATE OR REPLACE PROCEDURE test_varray
AS 
    TYPE varray_type IS VARRAY(20) OF INT; 
    v_varray varray_type; 
BEGIN 
    v_varray := varray_type(1, 2, 3);
    DBMS_OUTPUT.PUT_LINE('v_varray.first=' || v_varray.first); 
    DBMS_OUTPUT.PUT_LINE('v_varray.last=' || v_varray.last); 
END; 
/
```
执行结果：
```
call test_varray();
v_varray.first=1
v_varray.last=3
```
#### EXTEND
![](https://support.huaweicloud.com/devg-911-dws/public_sys-resources/note_3.0-zh-cn.png)
EXTEND函数主要是为了兼容Oracle的两种用法。在DWS中，数组会自动增长，EXTEND函数不是必须的。如果是新写的存储过程，完全没有必要使用EXTEND函数。
EXTEND函数可以对数组进行扩展，EXTEND有两种调用方式。
- 方式一： EXTEND包含一个整型入参，表示数组向后扩展size大小的长度，EXTEND后COUNT和LAST函数的值也会有相应的变化。
  用法如下：
  *varray*.EXTEND(size)
  其中*varray* .EXTEND这种无参的调用默认会向后扩展1位等价于*varray*.EXTEND(1)
  
- 方式二： EXTEND包含两个整型入参，第一个参数代表向后扩展size大小的长度，第二个参数表示扩展后的数组元素值和之下标为index的元素相同。
  用法如下：
  *varray*.EXTEND(size, index)
  
示例：
```
--演示在存储过程中对数组EXTEND函数的用法。
CREATE OR REPLACE PROCEDURE test_varray
AS 
    TYPE varray_type IS VARRAY(20) OF INT; 
    v_varray varray_type; 
BEGIN 
    v_varray := varray_type(1, 2, 3);
    v_varray.extend(3);
    DBMS_OUTPUT.PUT_LINE('v_varray.count=' || v_varray.count); 
    v_varray.extend(2,3);
    DBMS_OUTPUT.PUT_LINE('v_varray.count=' || v_varray.count); 
    DBMS_OUTPUT.PUT_LINE('v_varray(7)=' || v_varray(7)); 
    DBMS_OUTPUT.PUT_LINE('v_varray(8)=' || v_varray(8)); 
END; 
/
```
执行结果：
```
call test_varray();
v_varray.count=6
v_varray.count=8
v_varray(7)=3
v_varray(8)=3
```
#### NEXT和PRIOR
NEXT函数和PRIOR函数主要用于数组的循环遍历中，NEXT函数会根据入参index值，返回下一个数组元素的下标，若已经到达数组下标最大值则返回NULL。PRIOR函数会根据入参index值，返回上一个数组元素的下标，若已经到达数组下标最小值则返回NULL。
用法如下：
*varray*.NEXT(index)
*varray*.PRIOR(index)
示例：
```
--演示在存储过程中对数组NEXT和PRIOR函数的用法。
CREATE OR REPLACE PROCEDURE test_varray
AS 
    TYPE varray_type IS VARRAY(20) OF INT; 
    v_varray varray_type; 
    i int;
BEGIN 
    v_varray := varray_type(1, 2, 3);
    i := v_varray.COUNT;  
    WHILE i IS NOT NULL LOOP  
        DBMS_OUTPUT.PUT_LINE('test prior v_varray('||i||')=' || v_varray(i));  
        i := v_varray.PRIOR(i);  
    END LOOP; 
    i := 1;  
    WHILE i IS NOT NULL LOOP  
        DBMS_OUTPUT.PUT_LINE('test next v_varray('||i||')=' || v_varray(i));  
        i := v_varray.NEXT(i);  
    END LOOP;
END; 
/
```
执行结果：
```
call test_varray();
test prior v_varray(3)=3
test prior v_varray(2)=2
test prior v_varray(1)=1
test next v_varray(1)=1
test next v_varray(2)=2
test next v_varray(3)=3
```
#### EXISTS
EXISTS函数可以判断数组下标是否存在。
用法如下：
*varray*.EXISTS(index)
示例：
```
--演示在存储过程中对数组EXISTS函数的用法。
CREATE OR REPLACE PROCEDURE test_varray
AS 
    TYPE varray_type IS VARRAY(20) OF INT; 
    v_varray varray_type; 
BEGIN 
    v_varray := varray_type(1, 2, 3);
    IF v_varray.EXISTS(1) THEN
        DBMS_OUTPUT.PUT_LINE('v_varray.EXISTS(1)');
    END IF;
    IF NOT v_varray.EXISTS(10) THEN
        DBMS_OUTPUT.PUT_LINE('NOT v_varray.EXISTS(10)');
    END IF;
END; 
/
```
执行结果：
```
call test_varray();
v_varray.EXISTS(1)
NOT v_varray.EXISTS(10)
```
#### TRIM
TRIM函数可以从数组尾部删除指定数量的元素。
用法如下：
*varray*.TRIM(size)
其中*varray* .TRIM这种无参的调用会默认入参为1，等价于*varray*.TRIM(1)
示例：
```
--演示在存储过程中对数组TRIM函数的用法。
CREATE OR REPLACE PROCEDURE test_varray
AS 
    TYPE varray_type IS VARRAY(20) OF INT; 
    v_varray varray_type; 
BEGIN 
    v_varray := varray_type(1, 2, 3, 4, 5);
    v_varray.trim(3);
    DBMS_OUTPUT.PUT_LINE('v_varray.count' || v_varray.count);
    v_varray.trim;
    DBMS_OUTPUT.PUT_LINE('v_varray.count:' || v_varray.count);
END; 
/
```
执行结果：
```
call test_varray();
v_varray.count:2
v_varray.count:1
```
#### DELETE
DELETE函数可以从数组删除数组中的所有元素。
用法如下：
*varray* .DELETE或*varray*.DELETE()
示例：
```
--演示在存储过程中对数组DELETE函数的用法。
CREATE OR REPLACE PROCEDURE test_varray
AS 
    TYPE varray_type IS VARRAY(20) OF INT; 
    v_varray varray_type; 
BEGIN 
    v_varray := varray_type(1, 2, 3, 4, 5);
    v_varray.delete;
    DBMS_OUTPUT.PUT_LINE('v_varray.count:' || v_varray.count);
END; 
/
```
执行结果：
```
call test_varray();
v_varray.count:0
```
#### LIMIT
LIMIT函数可以返回数组的最大长度限制。
用法如下：
*varray* .LIMIT或*varray*.LIMIT()
示例：
```
--演示在存储过程中对数组LIMIT函数的用法。
CREATE OR REPLACE PROCEDURE test_varray
AS 
    TYPE varray_type IS VARRAY(20) OF INT; 
    v_varray varray_type; 
BEGIN 
    v_varray := varray_type(1, 2, 3, 4, 5);
    DBMS_OUTPUT.PUT_LINE('v_varray.limit:' || v_varray.limit);
END; 
/
```
执行结果：
```
call test_varray();
v_varray.limit:20
```
