2012-09-13 45 views
13

我有一個由表列的rowtype類型創建的關聯數組。爲什麼檢查PL/SQL中的空關聯數組失敗?

舉個例子,這是怎麼回事(表名稱不同,但結構是一樣的):

這是表的DDL

CREATE TABLE employees 
    (
    id  NUMBER, 
    name VARCHAR2(240), 
    salary NUMBER 
); 

這裏是我的程序是這樣做的:

DECLARE 
    TYPE table_of_emp 
     IS TABLE OF employees%ROWTYPE INDEX BY BINARY_INTEGER; 
    emp TABLE_OF_EMP; 
BEGIN 
    IF emp IS NULL THEN 
     dbms_output.Put_line('Null associative array'); 
    ELSE 
     dbms_output.Put_line('Not null'); 
    END IF; 
END; 

我假定這個應該被印刷結果在「空關聯數組」。但是,if條件失敗,執行跳轉到else部分。

現在,如果我把一個for循環打印集合值

DECLARE 
    TYPE table_of_emp 
     IS TABLE OF employees%ROWTYPE INDEX BY BINARY_INTEGER; 
    emp TABLE_OF_EMP; 
BEGIN 
    IF emp IS NULL THEN 
     dbms_output.Put_line('Null associative array'); 
    ELSE 
     dbms_output.Put_line('Not null'); 

     FOR i IN emp.first..emp.last LOOP 
      dbms_output.Put_line('Emp name: ' 
           || Emp(i).name); 
     END LOOP; 
    END IF; 
END; 

那麼程序單元引發了異常,引用for循環線

ORA-06502:PL/SQL :數字或數值錯誤

我認爲這是因爲空關聯數組。是否因爲空關聯數組而引發錯誤?

那麼爲什麼第一次檢查失敗呢?我究竟做錯了什麼?

數據庫服務器的Oracle 11g EE(版本11.2.0.3.0 64位)

+0

我沒有做過PLSQL一段時間,但在你的for循環('DBMS_OUTPUT.PUT_LINE(「的Emp名稱:」 || Emp(i).name);')'Emp(i).name'是'emp(i).name'? –

+0

@jschoen變量名不區分大小寫,因此無關緊要 – Sathya

回答

12

我認爲這將導致「空關聯數組」正在打印。這個假設對於關聯數組是錯誤的。它們在宣佈時存在,但是是空的。這將是其他類型的PL/SQL集合正確:

直到你初始化,一個嵌套表VARRAY是原子空; 集合本身是空的,而不是它的元素。要初始化嵌套表或varray,可以使用構造函數,系統定義的 函數具有與集合類型相同的名稱。該功能 從傳遞給它的元素構造集合。

您必須顯式調用每個varray和嵌套的 表變量的構造函數。關聯數組,第三種集合,做 不使用構造函數。只要功能 調用被允許,構造函數調用都是允許的。 Initializing and Referencing Collections

比較:

SQL> declare 
    2  type varchar2_100_aa is table of varchar2(100) index by binary_integer; 
    3  test varchar2_100_aa; 
    4 begin 
    5  test(1) := 'Hello'; 
    6  dbms_output.put_line(test(1)); 
    7 end; 
    8/
Hello 

PL/SQL procedure successfully completed. 

SQL> declare 
    2  type varchar2_100_va is varray(100) of varchar2(100); 
    3  test varchar2_100_va; 
    4 begin 
    5  test(1) := 'Hello'; 
    6  dbms_output.put_line(test(1)); 
    7 end; 
    8/
declare 
* 
ERROR at line 1: 
ORA-06531: Reference to uninitialized collection 
ORA-06512: at line 5 

變量數組做正確:

SQL> declare 
    2  type varchar2_100_va is varray(10) of varchar2(100); 
    3  test varchar2_100_va; 
    4 begin 
    5  test := varchar2_100_va(); -- not needed on associative array 
    6  test.extend; -- not needed on associative array 
    7  test(1) := 'Hello'; 
    8  dbms_output.put_line(test(1)); 
    9 end; 
10/
Hello 

PL/SQL procedure successfully completed. 

因爲關聯數組是空的firstlast爲空,這就是爲什麼你的第二個例子的結果ORA-06502: PL/SQL: Numeric or value error

SQL> declare 
    2  type varchar2_100_aa is table of varchar2(100) index by binary_integer; 
    3  test varchar2_100_aa; 
    4 begin 
    5  dbms_output.put_line(test.count); 
    6  dbms_output.put_line(coalesce(to_char(test.first), 'NULL')); 
    7  dbms_output.put_line(coalesce(to_char(test.last), 'NULL')); 
    8  test(1) := 'Hello'; 
    9  dbms_output.new_line; 
10  dbms_output.put_line(test.count); 
11  dbms_output.put_line(coalesce(to_char(test.first), 'NULL')); 
12  dbms_output.put_line(coalesce(to_char(test.last), 'NULL')); 
13 end; 
14/
0 
NULL 
NULL 

1 
1 
1 

PL/SQL procedure successfully completed. 

編輯另請注意,關聯數組可能很稀疏。循環使用firstlast之間的數字會引發任何稀疏集合的例外。而是使用firstnext像這樣:(Lastprev循環的另一個方向。)

SQL> declare 
    2  type varchar2_100_aa is table of varchar2(100) index by binary_integer; 
    3  test varchar2_100_aa; 
    4  i binary_integer; 
    5 begin 
    6  test(1) := 'Hello'; 
    7  test(100) := 'Good bye'; 
    8  dbms_output.put_line(test.count); 
    9  dbms_output.put_line(coalesce(to_char(test.first), 'NULL')); 
10  dbms_output.put_line(coalesce(to_char(test.last), 'NULL')); 
11  dbms_output.new_line; 
12 -- 
13  i := test.first; 
14  while (i is not null) loop 
15   dbms_output.put_line(to_char(i, '999') || ' - ' || test(i)); 
16   i := test.next(i); 
17  end loop; 
18 end; 
19/
2 
1 
100 

    1 - Hello 
100 - Good bye 

PL/SQL procedure successfully completed. 
+0

說得很清楚。感謝您的詳細解答! – Sathya

+2

這是正確的答案。但對我而言,它也突出了Oracle有時候的奇怪之處。當然,一個可變大小的「數組」(或PLSQL中的「表」)的整個意義在於,您不會事先知道在執行時有多少記錄(即它可能很容易爲零) 。而使用數組的全部意義在於讓你可以循環使用它們。因此,在能夠循環它之前,必須檢查該列表具有非零長度似乎完全違反直覺。爲什麼解析器不能簡單地循環零次,就像它在標準隱式遊標中一樣? – cartbeforehorse

5

我不會回答爲什麼要先檢查失敗。我從來沒有想過要做這樣的事情,並且對於它不會引發錯誤感到非常驚訝。

正如您所指出的,您在循環中引發異常的原因是,索引emp.first不存在。

而不是檢查空值,你應該真的在檢查這個索引的存在。你可以做使用.exists(i)語法:

if not emp.exists(emp.first) then 
    dbms_output.put_line('Nothing in here.'); 
end if; 
+0

Ben,Oracle文檔中提到「不能將EXISTS與關聯數組一起使用」。 - http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/collections.htm#CJAEFFID說過,程序單元成功運行。人力資源管理。 – Sathya

+0

是的@Sathya,對不起,錯過了你從表格rowtype中完成的事實。它運行成功嗎?這對我來說不合適?如果這是遊標的行類型,它將工作。 – Ben

+0

是的,當我編輯它,奇怪的是它運行成功,儘管文檔說明,否則 – Sathya