Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1702775
  • 博文数量: 136
  • 博客积分: 10021
  • 博客等级: 上将
  • 技术积分: 3261
  • 用 户 组: 普通用户
  • 注册时间: 2007-01-22 11:26
文章分类

全部博文(136)

文章存档

2010年(1)

2009年(26)

2008年(109)

我的朋友

分类: Oracle

2008-07-30 18:36:07

我们的一个报表模块出现问题:在 Oracle817 上执行统计数据正确,但在 Oracle816 上执行后只有最后一个统计数据。经跟踪,发现是 Oracle816 的一个 BUG,下面我们再现一下这个问题。
 
使用的匿名块如下,定义一个 VARRAY 数组,其元素为记录类型,注意粗体字的部分:
 
DECLARE
  -- 定义记录类型:学生(姓名、学号)
  TYPE RECORD_STUDENT IS RECORD(
    NAME VARCHAR2(20),
    score NUMBER(5,2) DEFAULT 0
  );
  -- 定义 VARRAY 数组:元素为学生
  TYPE TVAR_STUDENTS IS VARRAY(2) OF RECORD_STUDENT;
 
  rec_student RECORD_STUDENT;
  var_students TVAR_STUDENTS := TVAR_STUDENTS(rec_student, rec_student);
BEGIN
  -- score 赋值
  var_students(1).score := 1; 
  -- 输出 score 的值
  dbms_output.put_line(var_students(1).score);
  -- name 赋值
  var_students(1).NAME := 'yuecchaotian';
  -- 再次输出 score 的值
  dbms_output.put_line(var_students(1).score);
END;
 
1. 在 Oracle816 和 Oracle817 上分别执行该过程,结果一致:
 
SQL> select * from v$version;
 
BANNER
------------------------------------------------------
Oracle8i Release 8.1.6.0.0 - Production
PL/SQL Release 8.1.6.0.0 - Production
CORE    8.1.6.0.0       Production
TNS for 32-bit Windows: Version 8.1.6.0.0 - Production
NLSRTL Version 3.4.1.0.0 - Production
 
SQL> set serveroutput on
SQL> DECLARE
  2    -- 定义记录类型:学生(姓名、学号)
  3    TYPE RECORD_STUDENT IS RECORD(
  4      NAME VARCHAR2(20),
  5      score NUMBER(5,2) DEFAULT 0
  6    );
  7    -- 定义 VARRAY 数组:元素为学生
  8    TYPE TVAR_STUDENTS IS VARRAY(2) OF RECORD_STUDENT;
  9
 10    rec_student RECORD_STUDENT;
 11    var_students TVAR_STUDENTS
 12      := TVAR_STUDENTS(rec_student, rec_student);
 13  BEGIN
 14    -- score 赋值
 15    var_students(1).score := 1;
 16    -- 输出 score 的值:正确
 17    dbms_output.put_line(var_students(1).score);
 18    -- name 赋值
 19    var_students(1).NAME := 'yuecchaotian';
 20    -- 再次输出 score 的值:正确
 21    dbms_output.put_line(var_students(1).score);
 22  END;
 23  /
1
1
 
PL/SQL 过程已成功完成。
SQL> select * from v$version;
 
BANNER
----------------------------------------------------------
Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production
PL/SQL Release 8.1.7.0.0 - Production
CORE    8.1.7.0.0       Production
TNS for 32-bit Windows: Version 8.1.7.0.0 - Production
NLSRTL Version 3.4.1.0.0 - Production
 
SQL> set serveroutput on
SQL> DECLARE
  2    -- 定义记录类型:学生(姓名、学号)
  3    TYPE RECORD_STUDENT IS RECORD(
  4      NAME VARCHAR2(20),
  5      score NUMBER(5,2) DEFAULT 0
  6    );
  7    -- 定义 VARRAY 数组:元素为学生
  8    TYPE TVAR_STUDENTS IS VARRAY(2) OF RECORD_STUDENT;
  9
 10    rec_student RECORD_STUDENT;
 11    var_students TVAR_STUDENTS
 12      := TVAR_STUDENTS(rec_student, rec_student);
 13  BEGIN
 14    -- score 赋值
 15    var_students(1).score := 1;
 16    -- 输出 score 的值:正确
 17    dbms_output.put_line(var_students(1).score);
 18    -- name 赋值
 19    var_students(1).NAME := 'yuecchaotian';
 20    -- 再次输出 score 的值:正确
 21    dbms_output.put_line(var_students(1).score);
 22  END;
 23  /
1
1
 
PL/SQL 过程已成功完成。
 
2. 稍微改动一下:使用 VARRAY 数组的最第2个元素来存储,而不是第1个。这时在 Oracle816 下就出现问题了:
 
SQL> select * from v$version;
 
BANNER
------------------------------------------------------
Oracle8i Release 8.1.6.0.0 - Production
PL/SQL Release 8.1.6.0.0 - Production
CORE    8.1.6.0.0       Production
TNS for 32-bit Windows: Version 8.1.6.0.0 - Production
NLSRTL Version 3.4.1.0.0 - Production
 
SQL> set serveroutput on
SQL> DECLARE
  2    -- 定义记录类型:学生(姓名、学号)
  3    TYPE RECORD_STUDENT IS RECORD(
  4      NAME VARCHAR2(20),
  5      score NUMBER(5,2) DEFAULT 0
  6    );
  7    -- 定义 VARRAY 数组:元素为学生
  8    TYPE TVAR_STUDENTS IS VARRAY(2) OF RECORD_STUDENT;
  9
 10    rec_student RECORD_STUDENT;
 11    var_students TVAR_STUDENTS
 12      := TVAR_STUDENTS(rec_student, rec_student);
 13  BEGIN
 14    -- score 赋值
 15    var_students(2).score := 1;
 16    -- 输出 score 的值:正确
 17    dbms_output.put_line(var_students(2).score);
 18    -- name 赋值
 19    var_students(2).NAME := 'yuecchaotian';
 20    -- 再次输出 score 的值:错误
 21    dbms_output.put_line(var_students(2).score);
 22  END;
 23  /
1
0
 
PL/SQL 过程已成功完成。
SQL> select * from v$version;
 
BANNER
----------------------------------------------------------
Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production
PL/SQL Release 8.1.7.0.0 - Production
CORE    8.1.7.0.0       Production
TNS for 32-bit Windows: Version 8.1.7.0.0 - Production
NLSRTL Version 3.4.1.0.0 - Production
 
SQL> set serveroutput on
SQL> DECLARE
  2    -- 定义记录类型:学生(姓名、学号)
  3    TYPE RECORD_STUDENT IS RECORD(
  4      NAME VARCHAR2(20),
  5      score NUMBER(5,2) DEFAULT 0
  6    );
  7    -- 定义 VARRAY 数组:元素为学生
  8    TYPE TVAR_STUDENTS IS VARRAY(2) OF RECORD_STUDENT;
  9
 10    rec_student RECORD_STUDENT;
 11    var_students TVAR_STUDENTS
 12      := TVAR_STUDENTS(rec_student, rec_student);
 13  BEGIN
 14    -- score 赋值
 15    var_students(2).score := 1;
 16    -- 输出 score 的值:正确
 17    dbms_output.put_line(var_students(2).score);
 18    -- name 赋值
 19    var_students(2).NAME := 'yuecchaotian';
 20    -- 再次输出 score 的值:正确
 21    dbms_output.put_line(var_students(2).score);
 22  END;
 23  /
1
1
 
PL/SQL 过程已成功完成。
 
这就是这个报表模块遭遇的问题:在 Oracle816 中对 VARRAY 数组的最后一个元素一步步赋值时,每次赋值总会将上一个数据清零。所以我们的报表模块在 Oracle816 中执行后,只有最后一个统计数据,其余的数据全部为0。而在 Oracle817 及以上版本中不会这样。
 
3. 我们继续在 Oracle816 中测试,score 的结果“0”是从定义中被赋值的(default 0),那么我去掉 default 0 后看看:
 
SQL> DECLARE
  2    -- 定义记录类型:学生(姓名、学号)
  3    TYPE RECORD_STUDENT IS RECORD(
  4      NAME VARCHAR2(20),
  5      score NUMBER(5,2) --DEFAULT 0
  6    );
  7    -- 定义 VARRAY 数组:元素为学生
  8    TYPE TVAR_STUDENTS IS VARRAY(2) OF RECORD_STUDENT;
  9
 10    rec_student RECORD_STUDENT;
 11    var_students TVAR_STUDENTS
 12      := TVAR_STUDENTS(rec_student, rec_student);
 13  BEGIN
 14    -- score 赋值
 15    var_students(2).score := 1;
 16    -- 输出 score 的值:正确
 17    dbms_output.put_line(var_students(2).score);
 18    -- name 赋值
 19    var_students(2).NAME := 'yuechaotian';
 20    -- 再次输出 score 的值:正确
 21    dbms_output.put_line(var_students(2).score);
 22  END;
 23  /
1
1
 
PL/SQL 过程已成功完成。
 
这时却又执行正确了!
 
试了一下嵌套表,也会出现同样的问题:
 
SQL> DECLARE
  2    -- 定义记录类型:学生(姓名、学号)
  3    TYPE RECORD_STUDENT IS RECORD(
  4      NAME VARCHAR2(20),
  5      score NUMBER(5,2) DEFAULT 0
  6    );
  7    -- 定义 VARRAY 数组:元素为学生
  8    TYPE TVAR_STUDENTS IS TABLE OF RECORD_STUDENT;
  9
 10    rec_student RECORD_STUDENT;
 11    var_students TVAR_STUDENTS
 12      := TVAR_STUDENTS(rec_student, rec_student);
 13  BEGIN
 14    -- score 赋值
 15    var_students(2).score := 1;
 16    -- 输出 score 的值:正确
 17    dbms_output.put_line(var_students(2).score);
 18    -- name 赋值
 19    var_students(2).NAME := 'yuechaotian';
 20    -- 再次输出 score 的值:错误
 21    dbms_output.put_line(var_students(2).score);
 22  END;
 23  /
1
0
 
PL/SQL 过程已成功完成。
 
4. 解决办法
 
Oracle816 这个 BUG 不是很容易触发的,首先得是用记录类型作为元素来定义集合类型,必须带有默认值(default),也只有在操作最后一个元素时才会出现,并且最后一个元素的赋值方式是单个变量赋值,而不是整体赋值。看来这次比较背。
 
找到了触发这个 BUG 的条件,解决办法就很简单了:
 
  • 可以去掉默认值(default 0),就上刚才我们看到的那样;
  • 可以不使用 VARRAY 数组的最后一个元素。比如需要使用的长度为2,那么设置长度为3,但只使用前两个:
SQL> DECLARE
  2    -- 定义记录类型:学生(姓名、学号)
  3    TYPE RECORD_STUDENT IS RECORD(
  4      NAME VARCHAR2(20),
  5      score NUMBER(5,2) DEFAULT 0
  6    );
  7    -- 定义 VARRAY 数组:元素为学生
  8    TYPE TVAR_STUDENTS IS VARRAY(3) OF RECORD_STUDENT;
  9
 10    rec_student RECORD_STUDENT;
 11    var_students TVAR_STUDENTS
 12      := TVAR_STUDENTS(rec_student, rec_student, rec_student);
 13  BEGIN
 14    -- score 赋值
 15    var_students(2).score := 1;
 16    -- 输出 score 的值:正确
 17    dbms_output.put_line(var_students(2).score);
 18    -- name 赋值
 19    var_students(2).NAME := 'yuecchaotian';
 20    -- 再次输出 score 的值:正确
 21    dbms_output.put_line(var_students(2).score);
 22  END;
 23  /
1
1
 
PL/SQL 过程已成功完成。
  • 也可以使用整体赋值的方式,比如:
SQL> DECLARE
  2    -- 定义记录类型:学生(姓名、学号)
  3    TYPE RECORD_STUDENT IS RECORD(
  4      NAME VARCHAR2(20),
  5      score NUMBER(5,2) DEFAULT 0
  6    );
  7    -- 定义 VARRAY 数组:元素为学生
  8    TYPE TVAR_STUDENTS IS VARRAY(2) OF RECORD_STUDENT;
  9
 10    rec_student RECORD_STUDENT;
 11    var_students TVAR_STUDENTS
 12      := TVAR_STUDENTS(rec_student, rec_student);
 13  BEGIN
 14    rec_student.score := 1;
 15    rec_student.name := 'yuechaotian';
 16
 17    var_students(2) := rec_student;
 18
 19    dbms_output.put_line(var_students(2).name);
 20    dbms_output.put_line(var_students(2).score);
 21  END;
 22  /
yuechaotian
1
 
PL/SQL 过程已成功完成。
阅读(1297) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~