记录

记录由单行多列的标量构成,是一种复合数据结构。

PL/SQL支持三种记录类型:基于表的记录、基于游标的记录以及用户自定义的记录。

一、 基于表的记录

DECLARE
   emp_rec emp%rowtype;
BEGIN
   SELECT * INTO emp_rec FROM emp WHERE empno=7788;
DBMS_OUTPUT.PUT_LINE(
'Ename is: '||emp_rec.ename); DBMS_OUTPUT.PUT_LINE('Job is: '||emp_rec.job); DBMS_OUTPUT.PUT_LINE('Salary is: '||emp_rec.sal); END;

二、 基于游标的记录

DECLARE
  CURSOR emp_cur IS SELECT * FROM emp WHERE rownum <=4;
  emp_rec emp_cur%ROWTYPE;
BEGIN
  OPEN emp_cur;
  LOOP
     FETCH emp_cur INTO emp_rec;
     EXIT WHEN emp_cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(
'Ename is: '||emp_rec.ename); DBMS_OUTPUT.PUT_LINE('Job is: '||emp_rec.job); DBMS_OUTPUT.PUT_LINE('Salary is: '||emp_rec.sal); END LOOP; END;

三、 用户自定义的记录

创建用户自定义记录的语法如下所示:

TYPE type_name IS RECORD
   (field_name1 datatype1 [NOT NULL] [ := DEFAULT EXPRESSION],
    field_name2 datatype2 [NOT NULL] [ := DEFAULT EXPRESSION],
    ...
    field_nameN datatypeN [NOT NULL] [ := DEFAULT EXPRESSION]);
record_name TYPE_NAME;

试举一例:

DECLARE
  TYPE time_rec_type IS RECORD
     (curr_date DATE,
      curr_day  VARCHAR2(12),
      curr_time VARCHAR(8) :='00:00:00'
      );   
  time_rec TIME_REC_TYPE;
BEGIN
  SELECT sysdate INTO time_rec.curr_date FROM dual;
  time_rec.curr_day := TO_CHAR(time_rec.curr_date,'DAY');
  time_rec.curr_time := TO_CHAR(time_rec.curr_date,'HH24:MI:SS');
 
DBMS_OUTPUT.PUT_LINE(
'Date: '||time_rec.curr_date); DBMS_OUTPUT.PUT_LINE('Day: '||time_rec.curr_day); DBMS_OUTPUT.PUT_LINE('Time: '||time_rec.curr_time); END;

注意:

1> 当声明记录类型时,如果为某个字段指定了NOT NULL约束,则必须初始化该字段。譬如上例中,如果字段curr_date指定了NOT NULL约束,即curr_date DATE NOT NULL,则会报如下错误:PLS-00218: a variable declared NOT NULL must have an initialization assignment。可初始化为:curr_date DATE NOT NULL :=sysdate。

2> 记录不可以整体判断为空,只可以判断记录字段。

3> 记录不可以整体比较,只可以比较记录字段。

4> 可以把基于表或者游标的记录整体赋值给用户自定义的记录,只要两者具有相同的结构。但对于用户自定义的记录来说,即便两个自定义记录具有相同的结构,但类型却不同,不可整体赋值。如下所示:

DECLARE
  TYPE name_type1 IS RECORD
     (first_name VARCHAR2(15),
      last_name  VARCHAR2(30)
      );
  TYPE name_type2 IS RECORD
     (first_name VARCHAR2(15),
      last_name  VARCHAR2(15)
      );
   name_rec1 name_type1;
   name_rec2 name_type2;
BEGIN
   name_rec1.first_name := 'John';
   name_rec1.last_name := 'Smith';
   name_rec2 := name_rec1;
END;

运行报如下错误:

SQL> /
name_rec2 := name_rec1;
*
ERROR at line 15:
ORA-06550: line 15, column 17:
PLS-00382: expression is of wrong type
ORA-06550: line 15, column 4:
PL/SQL: Statement ignored

可见,两个记录虽然拥有相同的结构,但,每个记录的类型不同,该记录也是不兼容的,整体赋值就会报错。

上例可修改为:

DECLARE
  TYPE name_type1 IS RECORD
     (first_name VARCHAR2(15),
      last_name  VARCHAR2(30)
      );
   name_rec1 name_type1;
   name_rec2 name_type1;
BEGIN
   name_rec1.first_name := 'John';
   name_rec1.last_name := 'Smith';
   name_rec2 := name_rec1;
END;

 上述整体赋值的限制只局限于用户自定义记录类型。可以把基于表或者游标的记录整体赋值给用户自定义的记录,只要两者具有相同的结构。

DECLARE
  CURSOR dept_cur IS SELECT * FROM dept;
  TYPE dept_type IS RECORD
     (deptno dept.deptno%TYPE,
      dname  dept.dname%TYPE,
      loc    dept.loc%TYPE
      );
   dept_rec1 dept%ROWTYPE;   -- table-based record
   dept_rec2 dept_cur%ROWTYPE; -- cursor-based record
   dept_rec3 dept_type;
BEGIN
   -- Populate table-based record
   SELECT * INTO dept_rec1 FROM dept WHERE deptno=10;
   dept_rec2 := dept_rec1; 
   dept_rec3 := dept_rec1; 
 
   --Populate cursor-based record 
   OPEN dept_cur;
   LOOP
      FETCH dept_cur INTO dept_rec2;
      EXIT WHEN dept_cur%NOTFOUND;
   END LOOP;
   dept_rec1 := dept_rec2;
   dept_rec3 := dept_rec2;
   
   --Populate user defined record
   SELECT * INTO dept_rec3 FROM dept WHERE deptno=20;
   dept_rec1 := dept_rec3;
   dept_rec2 := dept_rec3;
END;

四、 嵌套记录

嵌套记录指的记录中包含其他记录和集合。

譬如:

DECLARE
  TYPE name_type IS RECORD
      (first_name  VARCHAR2(15);
       last_name   VARCHAR2(30));
  TYPE person_type IS RECORD
      (name   name_type;  
       street VARCHAR2(50);
       city   VARCHAR2(25);
       state  VARCHAR2(2);
       zip    VARCHAR2(5)); 
  person_rec person_type;
                     

这段代码包含两个用户自定义记录类型,其中,第二个用户自定义记录类型person_type是个嵌套的记录类型,因为其字段name是name_type类型的记录。

综合示例:

一、 输入部门号,输出该部门的员工

DECLARE
  TYPE ename_type IS TABLE OF emp.ename%TYPE INDEX BY BINARY_INTEGER;
  
  TYPE dept_info_type IS RECORD
      (deptno    emp.deptno%TYPE,
       ename_tab ename_type);
  
  CURSOR name_cur (p_deptno number) IS
     SELECT ename
       FROM emp
     WHERE deptno = p_deptno;
  
  dept_info_rec dept_info_type;
  v_deptno  NUMBER(2) := &v_deptno;
  v_counter INTEGER := 0;
BEGIN
  dept_info_rec.deptno := v_deptno;
  DBMS_OUTPUT.PUT_LINE('Deptno: '||dept_info_rec.deptno||chr(10)||'Employees:');
  
  FOR name_rec IN name_cur(v_deptno) LOOP
     v_counter := v_counter+1;
     dept_info_rec.ename_tab(v_counter) := name_rec.ename;
  END LOOP;
  
  FOR i IN 1..dept_info_rec.ename_tab.COUNT LOOP
      DBMS_OUTPUT.PUT_LINE(dept_info_rec.ename_tab(i));
  END LOOP;
END;

输出结果为: 

Enter value for v_deptno: 10
old 14: v_deptno NUMBER(2) := &v_deptno;
new 14: v_deptno NUMBER(2) := 10;
Deptno: 10
Employees:
CLARK
KING
MILLER

PL/SQL procedure successfully completed.

二、 输出每个部门的员工总数

DECLARE
  CURSOR dept_cur IS
    SELECT dname,loc,count(*) total
      FROM dept,emp
    WHERE dept.deptno=emp.deptno
    GROUP BY dname,loc;

  TYPE dname_rec_type IS RECORD
     (dname dept.dname%TYPE,
      loc   dept.loc%TYPE,
      total INTEGER);
  
  TYPE dept_type IS TABLE OF dname_rec_type INDEX BY BINARY_INTEGER;
  
  dept_tab dept_type;
  v_counter INTEGER :=0;
BEGIN
  FOR dept_rec IN dept_cur LOOP
     v_counter := v_counter+1;
     
     dept_tab(v_counter).dname := dept_rec.dname;
     dept_tab(v_counter).loc := dept_rec.loc;
     dept_tab(v_counter).total := dept_rec.total;
     
     DBMS_OUTPUT.PUT_LINE(dept_tab(v_counter).dname||' '||dept_tab(v_counter).loc
         ||' '||dept_tab(v_counter).total);
  END LOOP;
END;

输出结果为:

RESEARCH DALLAS 5
SALES CHICAGO 7
ACCOUNTING NEW YORK 3

PL/SQL procedure successfully completed.

posted @ 2014-09-01 18:33  iVictor  阅读(330)  评论(0编辑  收藏  举报