Oracle 管理用户对象依赖关系

背景:

项目组使用很多的procedure作为取数接口,现在需要重构底层取数过程,但是无法找出“底层取数procedure”被哪些“逻辑对象”(procedure, function…etc.)依赖。无法评估影响的范围,Java服务端可以通过调用层次分析出,但是直接在数据库端,进行调用时就很难分析。

通过查找官方文档,找到两个方法,现在分享给大家。

方法:

  1. utldtree.sql
  2. DBMS_UTILITY.get_dependency(type, schema, object_name);

utldtree

utldtree.sql是Oracle 提供的一个分析依赖的sql文件。

路径:%ORACLE_CLIENT%\RDBMS\ADMIN

因为没有包含在数据库标准包中,所以需要自己手动在数据库中执行utldtree下。

utldtree创建的objects:

序列:deptree_seq

1
2
> create sequence deptree_seq cache 200
>

表:deptree_temptab

1
2
3
4
5
6
7
8
> create table deptree_temptab
> (
> object_id number,
> referenced_object_id number,
> nest_level number,
> seq# number
> )
>

视图:deptree,ideptree

过程:deptree_fill

1
2
> create or replace procedure deptree_fill (type char, schema char, name char)
>

utldtree生成依赖关系步骤

生成依赖关系
1
2
3
4
5
6
7
8
-- 1.生成依赖关系
-- 说明:每一次调用会“清空”表DEPTREE_TEMPTAB
-- 参数说明:
-- type:对象类型(table,view,procedure...)
-- schema:当前用户名称
-- name:对象名称(待查询的被人依赖的对象)

CALL DEPTREE_FILL('PROCEDURE', 'FMIS0200', 'GET_ZWCS_DATA_KM');
查询依赖关系结果1
1
2
3
4
5
-- 2. 查询依赖关系结果
-- 说明:
-- 这个视图附带嵌套层次

SELECT * FROM DEPTREE D ORDER BY D.SEQ#;
查询依赖关系结果2
1
2
3
4
5
-- 3. 查询依赖关系结果
-- 说明:
-- 步骤:3和2一样,只是展示方式不同

SELECT * FROM IDEPTREE;

DBMS_UTILITY.get_dependency

oracle 11g R1开始提供管理object dependencies关系.

1
2
3
4
5
6
7
8
9
10
11
12
13
-- sql window 执行sql语句
-- 参数说明:
-- type:对象类型
-- schema:模式名称(用户名称)
-- name:object的对象名称
call dbms_utility.get_dependency('PROCEDURE', 'LALAL', 'TEST');

-- output tab页面查看结果
/*
DEPENDENCIES ON LALAL.TEST
------------------------------------------------------------------
...
*/

动态语句中的object依赖无法分析

动态的SQL语句中的依赖关系是无法分析出来。所以建议少用动态语句。

动态语句解决方法:ASK TOM上提供了间接的方法。

在创建procedure或者function中在declare部分,创建一个显示依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14

CREATE or replace PROCEDURE testproc IS
i PLS_INTEGER;
var_table varchar2(7):='TESTTAB';
-- 创建一个“直接”依赖,但是在过程中没有使用dummy变量。
cursor dummy is select * from TESTTAB;
BEGIN
-- 动态语句依赖的object(table对象)
execute immediate 'SELECT COUNT(*) FROM '||var_table||'' INTO i;
dbms_output.put_line(TO_CHAR(i));
END testproc;
/
-- 查询依赖
exec dbms_utility.get_dependency('TABLE', 'DWHADMIN','TESTTAB');

虽然“动态语句依赖”在依赖关系中无法分析,但是在procedure的declare部分显示依赖了TESTTAB,所以使用Oracle的依赖管理还是可以找到“依赖关系”。

1
2
3
4
5
> -- 声明“游标”,oracle是只会分配“游标的储存空间”
> -- 不会真正执行sql语句取数,如果你怕影响性能,可以加上where条件,取数为0条记录即可。
> -- 详情可以查看where does the Cursor stores the rows?
> cursor dummy is select * from TESTTAB;
>

显示依赖,但是不使用dummy变量,这是不好的实践。好的实践是要消灭动态SQL语句,因为动态带来了极大的维护成本。

附录-utldtree.sql(utldtree is stand for utility deep tree)

使用command window 执行它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
Rem 
Rem $Header: utldtree.sql,v 1.2 1992/10/26 16:24:44 RKOOI Stab $
Rem
Rem Copyright (c) 1991 by Oracle Corporation
Rem NAME
Rem deptree.sql - Show objects recursively dependent on given object
Rem DESCRIPTION
Rem This procedure, view and temp table will allow you to see all
Rem objects that are (recursively) dependent on the given object.
Rem Note: you will only see objects for which you have permission.
Rem Examples:
Rem execute deptree_fill('procedure', 'scott', 'billing');
Rem select * from deptree order by seq#;
Rem
Rem execute deptree_fill('table', 'scott', 'emp');
Rem select * from deptree order by seq#;
Rem
Rem execute deptree_fill('package body', 'scott', 'accts_payable');
Rem select * from deptree order by seq#;
Rem
Rem A prettier way to display this information than
Rem select * from deptree order by seq#;
Rem is
Rem select * from ideptree;
Rem This shows the dependency relationship via indenting. Notice
Rem that no order by clause is needed with ideptree.
Rem RETURNS
Rem
Rem NOTES
Rem Run this script once for each schema that needs this utility.
Rem
Rem MODIFIED (MM/DD/YY)
Rem rkooi 10/26/92 - owner -> schema for SQL2
Rem glumpkin 10/20/92 - Renamed from DEPTREE.SQL
Rem rkooi 09/02/92 - change ORU errors
Rem rkooi 06/10/92 - add rae errors
Rem rkooi 01/13/92 - update for sys vs. regular user
Rem rkooi 01/10/92 - fix ideptree
Rem rkooi 01/10/92 - Better formatting, add ideptree view
Rem rkooi 12/02/91 - deal with cursors
Rem rkooi 10/19/91 - Creation

drop sequence deptree_seq
/
create sequence deptree_seq cache 200 /* cache 200 to make sequence faster */
/
drop table deptree_temptab
/
create table deptree_temptab
(
object_id number,
referenced_object_id number,
nest_level number,
seq# number
)
/
create or replace procedure deptree_fill (type char, schema char, name char) is
obj_id number;
begin
delete from deptree_temptab;
commit;
select object_id into obj_id from all_objects
where owner = upper(deptree_fill.schema)
and object_name = upper(deptree_fill.name)
and object_type = upper(deptree_fill.type);
insert into deptree_temptab
values(obj_id, 0, 0, 0);
insert into deptree_temptab
select object_id, referenced_object_id,
level, deptree_seq.nextval
from public_dependency
connect by prior object_id = referenced_object_id
start with referenced_object_id = deptree_fill.obj_id;
exception
when no_data_found then
raise_application_error(-20000, 'ORU-10013: ' ||
type || ' ' || schema || '.' || name || ' was not found.');
end;
/

drop view deptree
/

set echo on

REM This view will succeed if current user is sys. This view shows
REM which shared cursors depend on the given object. If the current
REM user is not sys, then this view get an error either about lack
REM of privileges or about the non-existence of table x$kglxs.

set echo off
create view sys.deptree
(nested_level, type, schema, name, seq#)
as
select d.nest_level, o.object_type, o.owner, o.object_name, d.seq#
from deptree_temptab d, dba_objects o
where d.object_id = o.object_id (+)
union all
select d.nest_level+1, 'CURSOR', '<shared>', '"'||c.kglnaobj||'"', d.seq#+.5
from deptree_temptab d, x$kgldp k, x$kglob g, obj$ o, user$ u, x$kglob c,
x$kglxs a
where d.object_id = o.obj#
and o.name = g.kglnaobj
and o.owner# = u.user#
and u.name = g.kglnaown
and g.kglhdadr = k.kglrfhdl
and k.kglhdadr = a.kglhdadr /* make sure it is not a transitive */
and k.kgldepno = a.kglxsdep /* reference, but a direct one */
and k.kglhdadr = c.kglhdadr
and c.kglhdnsp = 0 /* a cursor */
/

set echo on

REM This view will succeed if current user is not sys. This view
REM does *not* show which shared cursors depend on the given object.
REM If the current user is sys then this view will get an error
REM indicating that the view already exists (since prior view create
REM will have succeeded).

set echo off
create view deptree
(nested_level, type, schema, name, seq#)
as
select d.nest_level, o.object_type, o.owner, o.object_name, d.seq#
from deptree_temptab d, all_objects o
where d.object_id = o.object_id (+)
/

drop view ideptree
/
create view ideptree (dependencies)
as
select lpad(' ',3*(max(nested_level))) || max(nvl(type, '<no permission>')
|| ' ' || schema || decode(type, NULL, '', '.') || name)
from deptree
group by seq# /* So user can omit sort-by when selecting from ideptree */
/

引用

how to find the dependencies of table?

where does the Cursor stores the rows?

6-schema object dependencies (Oracle Database Concepts, 11g Release 1)