1. 程式人生 > >PL/SQL記錄和表(oracle type(陣列))(最簡單!文章下面標黃標題是精華,必看)

PL/SQL記錄和表(oracle type(陣列))(最簡單!文章下面標黃標題是精華,必看)

PL/SQL記錄和表
1.使用%TYPE  (用於基本資料型別)
例1(使用者並不知道Course_No的資料型別,只知道他是基於Student_No資料型別的,隨著Student_No的型別變化而變化):
DECLARE
...
Student_No INTEGER;
...
Course_No Student_No%TYPE;

例2(表示BrowsePath的資料型別是基於表BookInfo中的StoredPath列的資料型別):
DECLARE
...
BrowsePath BookInfo.StoredPath%TYPE;
...

2.記錄型別(注意,我自己實驗,這裡用IS RECORD會報錯,要用create or replace type .. is object !!)

語法:
TYPE 記錄型別 IS RECORD
( 欄位1 資料型別 [NOT NULL] {DEFAULT |:= } 表示式 ]
  [  ,  欄位2  ……]
)

例子(全手打啊!!)

(下面一整個例子要一次性執行,語法是:
DECLARE
...
BEGIN
...
END;)


SET SERVEROUTPUT ON
DECLARE
 TYPE StudentRecord IS RECORD (
 StudentNo INTEGER,
 Name VARCHAR2(10),
 Study VARCHAR2(10),
 Activity Study%TYPE
 );

 Student1 StudentRecord;

PROCEDURE PrintStudentRecord(StudentA IN StudentRecord) IS
BEGIN
 DBMS_OUTPUT.enable;
 DBMS_OUTPUT.put('學號:  '|| StudentA.StudentNo);    --DBMS_OUTPUT.put是不換行
 DBMS_OUTPUT.put('姓名:  '|| StudentA.Name);
 if StudentA.Study = 'Excellent' then
  DBMS_OUTPUT.put('學習成績 優秀');
 elsif StudentA.Study = 'Good' then
  DBMS_OUTPUT.put('學習成績 良好');
 end if;
 if StudentA.Activity = 'Excellent' then
  DBMS_OUTPUT.put('活動成績 優秀');
 elsif StudentA.Activity = 'Good' then
  DBMS_OUTPUT.put('活動成績 良好');
 end if;
 DBMS_OUTPUT.put_line('       一條學生資訊列印完畢');
END PrintStudentRecord;

BEGIN
 Student1.StudentNo := 112;
 Student1.Name := '王飄飄';
 Student1.Study := 'Excellent';
 Student1.Activity := 'Good';

 PrintStudentRecord(Student1);
END;

3.使用%ROWTYPE (用於記錄型別和表結構)
(表明變數InputStudent是與表StudentInfo的欄位一致的記錄變數):
DECLARE
...
InputStudent StudentInfo%ROWTYPE;
...

4.表
語法:
TYPE PL/SQL表名 IS TABLE OF 資料型別

例1(宣告的StudentName表是由和StudentInfo表中Name列相同型別的資訊組成的)
DECLARE
...
TYPE StudentName  IS TABLE OF StudentInfo.Name%TYPE;
...
例2(宣告的StudentName表是由資料型別為CHAR(20)的列資訊組成的)
DECLARE
...
TYPE StudentName  IS TABLE OF CHAR(20) INDEX BY BINARY_INTEGER;
...

我的提問(我要定義一個String陣列,有2個欄位,是個表(不止一行(record),有多行;Table只允許有一個欄位,怎麼辦呢)):
type .. is table of(a varchar(20),b varchar(20))這樣貌似不行 。
只能
type .. is record(a varchar(20),b varchar(20))

但是我現在需要用type做一個String陣列的功能,應該是用table的,但table語法就是
 ..is table of資料型別,沒法放2個欄位,怎麼辦

解答(貌似有問題):
type T_tbl is record(
            a     NUMBER(16),
            b     NUMBER(16));
type v_table is table of T_tbl;
v_arr v_table:=v_table();

我的實驗:

1.建立type of object(用create or replace語法時,要用object;用declare ..begin ..end 時,可以直接用type.. is record 。不是record,它會報錯)

建立法1:

create or replace type ty_mxklrownum is object(
            index_rum number(5,0),sendtype char(1));

(此建立法如果寫成下面那樣就會報錯:

create or replace  type ty_mxklrownum isrecord(
            index_rum number(5,0),sendtype char(1));

若要正確,則用下面語法:

declare

    type ty_mxklrownum is record(

       index_rum number(5,0),sendtype char(1));

begin

  .....

end;

2.建立type is table of

create or replace type ty_table_mxklrownum as table of ty_mxklrownum; 

3.建立測試用儲存過程(入參錯了,應該是table,但是不知道里面怎麼用所以直接忽略了)
create or replace PROCEDURE PrintStudentRecord(StudentA IN ty_mxklrownum) IS
BEGIN
 DBMS_OUTPUT.enable;
 DBMS_OUTPUT.put('行號:  '|| StudentA.index_rum);    --DBMS_OUTPUT.put是不換行
 DBMS_OUTPUT.put('type:  '|| StudentA.sendtype);
 DBMS_OUTPUT.put_line('       一條資訊列印完畢');
END PrintStudentRecord;

4.命令視窗測試:
SET SERVEROUTPUT ON
declare  
    Student1   ty_table_mxklrownum;  
begin  
    Student1   :=   ty_table_mxklrownum();  
    Student1.extend(10);  
    Student1(1) := ty_mxklrownum(1,'0');
 PrintStudentRecord(Student1);
end;

 一點筆記:

TYPE TABLE_COL IS TABLE OF VARCHAR2(4) INDEX BY BINARY_INTEGER;
column_select      TABLE_COL;
這兩句是什麼意思?
答:
TABLE 相當於是陣列,這裡定義了一個數組型別TABLE_COL
INDEX BY BINARY_INTEGER這裡是定義陣列下標是整數,因為ORACLE中下標可以是字串.
 VARCHAR2(4)這是定義數組裡面只能放字串
column_select TABLE_COL; 定義了一個TABLE_COL型別的變數column_select
因為TABLE_COL被定義成了陣列型別,所以column_select
就相當於是一個數組啦.


定義一個聯合陣列,
TABLE OF VARCHAR2 (4000), 是陣列存在資料的型別.

還比如:
Type Syncdata Is Table Of Varchar2(4000) Index By Varchar2(64);
定義一個聯合陣列,
Table Of Varchar2(4000)是陣列存在資料的型別,
Index By Varchar2(64)是陣列下標的型別。

再比如還可以這樣:
type TDictRecord is record
  (
  New_Code varchar2(256),
  New_Name varchar2(1024),
  Old_Name varchar2(1024)
  );

  --dict data
  type TDictData is table of TDictRecord index by varchar2(256);
定義一個聯合陣列,
table of TDictRecord 是陣列存在資料的型別,
index by varchar2(256) 是陣列下標的型別 指定為字串型 (varchar)

--本文件可以直接拷貝執行。

/*
oracle複合資料型別
PL/SQL有兩種複合資料結構:記錄和集合。記錄由不同的域組成,集合由不同的元素組成。
*/

/*
一、記錄型別

類似C語言中的結構,有兩種定義方式:顯式定義和隱式定義。
*/

create table test
(
id varchar2(20),
mc varchar2(60)
);

insert into test values('111','11111');
insert into test values('222','22222');
insert into test values('333','33333');
insert into test values('444','44444');
insert into test values('555','55555');
insert into test values('666','66666');

commit;

/*

1.1、顯示定義記錄

*/

set serverout on
declare
type t_record is record
(
id test.id%type,
mc test.mc%type
);
var_record t_record;
counter number default 0;
begin
        for row_test in (select id,mc from test) loop
                counter := counter + 1;
                var_record.id := row_test.id;
                var_record.mc := row_test.mc;
                dbms_output.put_line('var_record:'||var_record.id||'---'||var_record.mc);
                dbms_output.put_line('row_test:'||row_test.id||'---'||row_test.mc);
                dbms_output.put_line('================loop '||counter||' times.');
        end loop;
        exception when others then
                dbms_output.put_line(sqlcode||sqlerrm);
end;
/

/*有一些PL/SQL指令在使用隱式定義記錄時沒有使用%ROWTYPE屬性,比如遊標FOR迴圈或觸發器中的ld和:new記錄。*/

/*

1.2、隱式定義記錄

*/

隱式定義記錄中,我們不用描述記錄的每一個域,在宣告記錄變數時使用%ROWTYPE命令定義與資料庫表,檢視,遊標有相同結構的記錄。

declare
t_record1 test%rowtype;
cursor cur_test(v_id in varchar2) is
select id,mc from test
where id <= v_id;
t_record2 cur_test%rowtype;
begin
        for row_test in cur_test('333') loop
                t_record1.id := row_test.id;
                t_record1.mc := row_test.mc;
                t_record2.id := row_test.id;
                t_record2.mc := row_test.id;
                dbms_output.put_line('t_record1:'||t_record1.id||'---'||t_record1.mc);
                dbms_output.put_line('t_record2:'||t_record2.id||'---'||t_record2.mc);
                dbms_output.put_line('row_test:'||row_test.id||'---'||row_test.mc);
                dbms_output.put_line('================loop '||cur_test%rowcount||' times.');
        end loop;
        exception when others then
                dbms_output.put_line(sqlcode||sqlerrm);
end;
/

/*

二、集合

類似C語言中的陣列,在ORACLE7.3及以前的版本中只有一種集合稱為PL/SQL表,這種型別的集合依然保留,就是索引(INDEX_BY)表。
PL/SQL有三種類型的集合

a、Index_by表
b、巢狀表
c、VARRAY


三種類型的集合之間的差異,包括資料繫結、稀疏性(sparsity)、資料庫中的儲存能力都有不相同。

資料繫結:
        繫結涉及到集合中元素數量的限制,VARRAY集合中的元素的數量是有限,Index_by和巢狀表則是沒有限制的。
稀疏性(sparsity):
        稀疏性描述了集合的下標是否有間隔,Index_by表和巢狀表可以是稀疏的,VARRAY型別的集合則是緊密的,它的下標之間沒有間隔。
儲存:
        Index_by表不能儲存在資料庫中,但巢狀表和VARRAY可以被儲存在資料庫中。
       
Index_by表定義語法如下:
        TYPE type_name IS TABLE OF element_type [NOT NULL] INDEX BY BINARY_INTEGER;
        關鍵字是INDEX BY BINARY_INTEGER,沒有這個關鍵字,那麼集合將是一個巢狀表。由於不儲存在資料庫中,
        element_type可以是任何合法的PL/SQL資料型別,包括:PLS/INTEGER、SIGNTYPE、和BOOLEAN。

巢狀表定義語法如下:
        巢狀表非常類似於Index_by表,建立的語法也非常相似。只是沒有INDEX BY BINARY_INTEGER子串
        TYPE type_name IS TABLE OF element_type [NOT NULL];
        儲存在一個數據庫中的巢狀表並不與表中的其它資料存放在同一個資料塊中,它們實際上被存放在第二個表中。
        從資料庫中取回的巢狀表也不保證元素的順序。集合資料是離線儲存的,所以巢狀表適合大型集合。
       
VARRAY定義語法如下:
        TYPE type_name IS [VARRAY|VARYING ARRAY] (max_size) OF element_type [NOT NULL];
                max_size是一個整數,用於標示VARRAY集合擁有的最多元素數目。VARRAY集合的元素數量可以低於max_size,但不能超過max_size。
                element_type是一維元素的資料型別,如果element_type是記錄,那麼這個記錄只能使用標量資料欄位(與巢狀標相似)。
                VARRAY儲存在資料庫中時與表中的其他資料存放在同一個資料塊中,元素的順序儲存在VARRAY中。
                集合是線上儲存的,VARRAY很適合於小型集合。
               
巢狀表和VARRAY都能作為列儲存在資料庫表中,所以集合自身可以為NULL,當集合為NULL時,使用者也不能引用集合中的元素。
使用者可以使用IS NULL操作符檢測集合是否為NULL。

1、index_by表:

*/

declare
cursor cur_test is select id,mc from test;
type t_test1 is table of varchar2(60) index by binary_integer;
type t_test2 is table of test%rowtype index by binary_integer;
var_test1 t_test1;
var_test2 t_test2;
var_new t_test2;
begin
SELECT id,mc INTO var_test2(0) FROM test WHERE id='111';
dbms_output.put_line('var_test2(0):'||var_test2(0).id||'---'||var_test2(0).mc);
SELECT id,mc INTO var_test2(8) FROM test WHERE id='333';
dbms_output.put_line('var_test2(8):'||var_test2(8).id||'---'||var_test2(8).mc);
var_new := var_test2;
dbms_output.put_line('=====  copy var_test2 to var_new  =====');
dbms_output.put_line('var_new(0):'||var_new(0).id||'---'||var_new(0).mc);
dbms_output.put_line('var_new(8):'||var_new(8).id||'---'||var_new(8).mc);
end;
/

/*
巢狀表和VARRAY由建構函式初始化

2、巢狀表和VARRAY:

*/

DECLARE
TYPE t_test1 IS TABLE OF test.id%TYPE;
TYPE t_test2 IS VARRAY (10) OF test.id%TYPE;
var_test1 t_test1;
var_test2 t_test2;
begin
        --var_test1(1) := ('test1.1'); --沒有初始化不能賦值
        var_test1 := t_test1('test1.1','test1.2','test1.3');
        dbms_output.put_line('var_test1: '||var_test1(1)||','||var_test1(2)||','||var_test1(3));
        var_test2 := t_test2('test2.1','test2.2','test2.3');
        dbms_output.put_line('var_test2: '||var_test2(1)||','||var_test2(2)||','||var_test2(3));
        var_test1(2) := 'test1.2_update';
        dbms_output.put_line('====  修改了var_test1(2)  ====');
        dbms_output.put_line('var_test1: '||var_test1(1)||','||var_test1(2)||','||var_test1(3));
        dbms_output.put_line(var_test1.next(3));
        dbms_output.put_line('var_test2元素個數: '||var_test2.limit());
end;
/

/*
巢狀表的元素可以是集合,注意賦值的時候是varray_element.record_column := 的形式.
除了建構函式外,集合還有很多內建函式,按照面向物件編成的叫法稱之為方法。
方法==========描述====================================================================使用限制
COUNT=========返回集合中元素的個數   
DELETE========刪除集合中所有元素  
DELETE(x)=====刪除元素下標為x的元素===================================================對VARRAY非法
DELETE(x,y)===刪除元素下標從X到Y的元素================================================對VARRAY非法
EXIST(x)======如果集合元素x已經初始化,則返回TRUE, 否則返回FALSE  
EXTEND========在集合末尾新增一個元素==================================================對Index_by非法
EXTEND(x)=====在集合末尾新增x個元素===================================================對Index_by非法
EXTEND(x,n)===在集合末尾新增元素n的x個副本============================================對Index_by非法
FIRST=========返回集合中的第一個元素的下標號,對於VARRAY集合始終返回1。  
LAST==========返回集合中最後一個元素的下標號, 對於VARRAY返回值始終等於COUNT.   
LIMIT=========返回VARRY集合的最大的元素個數===========================================Index_by集合和巢狀表無用
NEXT(x)=======返回在第x個元素之後及緊挨著它的元素的值,如果x是最後一個元素,返回null.  
PRIOR(x)======返回在第x個元素之前緊挨著它的元素的值,如果x是第一個元素,則返回null。  
TRIM==========從集合末端開始刪除一個元素==============================================對於index_by不合法
TRIM(x)=======從集合末端開始刪除x個元素===============================================對index_by不合法

*/

/*

三、綜合例項BULK COLLECT的用法

*/

set serverout on
DECLARE
TYPE t_record IS RECORD (
id number(18,0),
mc varchar2(50)
);
var_record t_record;
type t_test is table of t_record;
var_test t_test := t_test();
cursor cur_test is select id,mc from test;
begin
open cur_test;
fetch cur_test BULK COLLECT INTO var_test;
for i in 1..var_test.count() loop
        dbms_output.put_line(var_test(i).id||'---'||var_test(i).mc);
end loop;
end;
/

/*

一個oracle函式返回陣列的例子

今天給一個朋友寫了個函式返回陣列的例子,順便貼出來。

create or replace type t_ret_table is table of varchar2(20);

create or replace function f_test(var_num in integer) return t_ret_table is
var_out t_ret_table;
begin
        var_out := t_ret_table();
        var_out.extend(var_num);
        for i in 1..var_num loop
                var_out(i) := i;
        end loop;
        return var_out;
end f_test;
/



set serverout on
declare
aa t_ret_table;
begin
aa := f_test(10);
for i in 1..aa.count loop
        dbms_output.put_line(aa(i));
end loop;
end;
/

*/