从 SQL 查询列表中提取表名的简单方法
设置
假设这是您的查询列表:
select * from a where id = 1;
select * from v;
with my_id as (select id from b)
select /*+ PARALLEL(4) */ * from a where id in (select id from my_id);
为了也涵盖视图访问,这里是表定义:
create table a (id number);
create index a_idx on a(id);
create table b (id number);
create table c (id number);
create table d (id number);
create view v as
select c.id from c join d on c.id = d.id;
所以有表a, b, c and d 和v 在c and d 上查看
*因此,您希望看到一个表列表 a, b, c and d,即使在您的查询列表中也只有表 a, b 被直接引用。
解决方案
在第一步中,将您的查询列表转换为EXPLAIN PLAN 语句列表。如果列表的长度很长,您可能会发现使用某种脚本是可行的:
EXPLAIN PLAN SET STATEMENT_ID = 'q1' into plan_table FOR select * from a where id = 1;
EXPLAIN PLAN SET STATEMENT_ID = 'q2' into plan_table FOR select * from v;
EXPLAIN PLAN SET STATEMENT_ID = 'q3' into plan_table FOR with my_id as (select id from b)
select /*+ PARALLEL(4) */ * from a where id in (select id from my_id);
通过执行EXPLAIN PLAN 语句,plan_table 填充了执行计划,每个操作的访问对象存储在列OBJECT_OWNER, OBJECT_NAME 中。
第一个天真的查询返回这个列表:
select distinct OBJECT_OWNER, OBJECT_NAME
from plan_table
where statement_id in ('q1','q2','q3') and OBJECT_NAME is not NULL
order by 1,2;
OBJECT_OWNER OBJECT_NAME
------------ -----------
DWH A_IDX
DWH B
DWH C
DWH D
SYS :TQ10000
SYS :TQ10001
SYS :TQ10002
有两件事必须解决。
首先必须忽略并行执行的行源(例如:TQ10000),因为它们不是真正的表。
其次,索引名称(例如A_IDX)必须使用字典视图DBA_INDEXES映射到table_names。
请注意,您不需要特别注意视图,因为它们会自动解析为源表。 子查询分解(with 子句)也是如此。
两个主题的查询如下所示:
select distinct OBJECT_OWNER,
/* map index name to table name */
case when p.OPERATION = 'INDEX' then i.TABLE_NAME else p.OBJECT_NAME end as TABLE_NAME
from plan_table p
left outer join dba_indexes i on p.OBJECT_OWNER = i.OWNER and p.OBJECT_NAME = i.INDEX_NAME
where statement_id in ('q1','q2','q3') and OBJECT_NAME is not NULL
and p.OBJECT_NAME not like ':%' /* suppress parallel row sources */
order by 1,2;
OBJECT_OWNER TABLE_NAME
------------ ----------
DWH A
DWH B
DWH C
DWH D
最后请注意,此方法仅在所有查询和视图在语法上有效并且您拥有足够权限的用户来解释列表中所有查询的计划时才有效。