当一个存储过程所依赖(引用的)对象发生某些更改时,会使得存储过程失效(invalidated),比如存储过程依赖(引用)的表增加减少了列、存储过程依赖(引用)的存储过程被重新编译。本文将介绍一种特殊的会引起存储过程失效的情况。

下面通过测试来演示与DB LINK相关的存储过程失效的情况:

1. 在TEST用户下创建到db1的DB LINK:

SQL> create database link to_db connect to perfstat identified by xxx using 'db1';

数据库链接已创建。

2.在TEST2用户下创建到db2的DB LINK,DB LINK的名称仍然为to_db,但实际上连接的数据库与TEST用户下to_db这个DB LINK连接的数据库并不是同一个数据库:

SQL> create database link to_db connect to perfstat identified by xxx using 'db2';

数据库链接已创建。

3. 在TEST用户下创建存储过程TEST_P1:

SQL> create or replace procedure test_p1
  2  is
  3    v_dbid number;
  4  begin
  5    select dbid into v_dbid from stats$snapshot@to_db where rownum<2;
  6  end;
  7  /

过程已创建。

SQL> select object_id,status from user_objects where object_name='TEST_P1';

 OBJECT_ID STATUS
---------- ----------
     18443 VALID

4. 在TEST2用户下创建存储过程TEST2_P1,这里存储过程TEST2_P1的代码与TEST用户下存储过程TEST_P1的代码明显不同。这两个存储过程的共同点是都引用了STATS$SNAPSHOT@TO_DB这个远程表。由于TEST和TEST2用户下TO_DB连接的是不同的数据库,因此这2个存储过程引用的STATS$SNAPSHOT@TO_DB位于不同的数据库上,也就是说是不同的表

SQL> create or replace procedure test2_p1
  2  is
  3    v_snap_time date;
  4  begin
  5    select snap_time into v_snap_time from stats$snapshot@to_db where rownum<2;
  6  end;
  7  /

过程已创建。

SQL> select object_id,status from user_objects where object_name='TEST2_P1';

 OBJECT_ID STATUS
---------- ----------
     18445 VALID

5. 在TEST用户下查看存储过程TEST_P1的状态,发现其状态为INVALID,而实际上这个时候这个存储过程以及其引用的对象没有任何变更:

SQL> select object_id,status from user_objects where object_name='TEST_P1';

 OBJECT_ID STATUS
---------- ----------
     18443 INVALID

6. 如果重新编译TEST.TEST_P1,那么其状态会成为VALID,但是这个时候TEST2.TEST2_P1则又莫名其妙地变成为INVALID:

SQL> alter procedure test.test_p1 compile;

过程已更改。

SQL> select object_id,owner,object_name,status from dba_objects 
  where object_name in ('TEST_P1','TEST2_P1');

 OBJECT_ID OWNER           OBJECT_NAME                    STATUS
---------- --------------- ------------------------------ ----------
     18443 TEST            TEST_P1                        VALID
     18445 TEST2           TEST2_P1                       INVALID

7. 在TEST2用户下执行存储过程TEST2_P1,然后再次检查存储过程状态:

SQL> exec test2_p1

PL/SQL 过程已成功完成。

SQL> select object_id,owner,object_name,status from dba_objects
  2  where object_name in ('TEST_P1','TEST2_P1');

 OBJECT_ID OWNER           OBJECT_NAME                    STATUS
---------- --------------- ------------------------------ ----------
     18443 TEST            TEST_P1                        INVALID
     18445 TEST2           TEST2_P1                       VALID

可以看到,TEST.TEST_P1失效了,而TEST2.TEST2_P1又正常了。

在这里就可以提出问题了:为什么创建了TEST2.TEST2_P1这个存储过程之后,TEST.TEST_P1就失效了?为什么TEST.TEST_P1重新编译之后TEST2.TEST2_P1又失效了?为什么TEST2.TEST2_P1重新执行(有一个隐式的编译过程)后,TEST.TEST_P1又失效了?

其实上以上三个问题可以归纳为一个问题:为什么TEST.TEST_P1和TEST2.TEST2_P1这2个存储过程,在其中一个状态正常的情况下,另一个为失效状态?

Read the rest of this entry