记录

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

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 @   iVictor  阅读(339)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示