【问题标题】:Oracle - get table name from sql textOracle - 从 sql 文本中获取表名
【发布时间】:2016-05-19 15:20:44
【问题描述】:

我在一个带有简单 sql 查询的表中有一个列,我想从它们中 regexp_substr 表名。 文本示例:

SELECT PT.PT_PARTY_NAME VALUE,PT.PT_PARTY_NAME LABEL
    FROM DWH_OWNER.DWH_ACCOUNTS ACC,
         DWH_OWNER.DWH_PARTIES PT
   WHERE ACC.ACC_SOURCE_ID = :P_DOMVAL_REF1
    AND ACC.ACC_PT_KEY = PT.PT_KEY

SELECT DISTINCT QRMPT_PRODUCT VALUE, QRMPT_PRODUCT LABEL FROM   
DWH_OWNER.DWH_QRM_PRODUCT_TYPES ORDER BY VALUE

所以,我需要找到“FROM”,跳过一些空格然后得到第一个单词。 我暂时对第二张桌子不感兴趣。

我做了什么:

select 
    sql, 
    regexp_substr(upper(sql), '(\s)FROM(\s)*([[:alnum:]]|\.|_)*') tablename
from my_table_with_queries;

输出:

 FROM DWH_OWNER.DWH_ACCOUNTS
FROM   
    DWH_OWNER.DWH_QRM_PRODUCT_TYPES

我的 qyery 出了什么问题: 它在表名前输出带有“FROM”的表名。我想要直接的表名,没有空格。

期望的输出:

DWH_OWNER.DWH_ACCOUNTS
DWH_OWNER.DWH_QRM_PRODUCT_TYPES

编辑: 我设法在上一步中完成regexp_substr(tablename, '(\w|_|\.)+', 1,2) 来清理表名。但是有可能使用单个正则表达式获得所需的输出吗?

Here 应该是一个 sqlfiddle,但该站点目前无法运行。查询:

  with a as (
select 'SELECT PT.PT_PARTY_NAME VALUE,PT.PT_PARTY_NAME LABEL
    FROM DWH_OWNER.DWH_ACCOUNTS ACC,
         DWH_OWNER.DWH_PARTIES PT
   WHERE ACC.ACC_SOURCE_ID = :P_DOMVAL_REF1
    AND ACC.ACC_PT_KEY = PT.PT_KEY' sql from dual
 union all

 select 'SELECT DISTINCT QRMPT_PRODUCT VALUE, QRMPT_PRODUCT LABEL FROM   
DWH_OWNER.DWH_QRM_PRODUCT_TYPES ORDER BY VALUE' from dual
  )

select 
       regexp_substr(upper(sql), '\sFROM\s*(\w|\.|_)*') tablename, sql
from a;

【问题讨论】:

  • 能不能简化成\sFROM\s*([\w.]*)
  • @Thomas 稍作修改 - regexp_substr(sql,'\sFROM\s*(\w|\.)*') - 是的,但仍会输出 FROM。那是我的问题。
  • 你为什么在\s周围使用捕获组?
  • 我不知道什么是捕获组。我是正则表达式的初学者。不是使用 Oracle,而是使用正则表达式是 :)
  • 您会在某个时候想要第二张桌子吗?您是否需要允许 CTE 和内联视图以及子查询?最终目标是什么 - 找到语句引用的所有表?

标签: regex oracle regexp-substr


【解决方案1】:

与其尝试编写自己的解析器,不如让 Oracle 通过explain plan 为您解析,然后查看计划表以查看它引用了哪些对象:

declare
  text varchar2(4000) := 'SELECT PT.PT_PARTY_NAME VALUE,PT.PT_PARTY_NAME LABEL
    FROM DWH_OWNER.DWH_ACCOUNTS ACC,
         DWH_OWNER.DWH_PARTIES PT
   WHERE ACC.ACC_SOURCE_ID = :P_DOMVAL_REF1
    AND ACC.ACC_PT_KEY = PT.PT_KEY';
begin
  execute immediate 'explain plan for ' || text;
end;
/

select distinct object_owner, object_name
from plan_table
where object_type = 'TABLE';

OBJECT_OWNER                   OBJECT_NAME                  
------------------------------ ------------------------------
DWH_OWNER                      DWH_ACCOUNTS                  
DWH_OWNER                      DWH_PARTIES                   

正如@Aleksej 建议的那样,如果优化器仅使用索引(因此执行计划显示索引访问/扫描而不命中表,因为所有相关列都在索引中)然后计划表只报告索引。您可以通过加入索引视图来实现这一点;如果它也打到桌子上,它只会为两者报告:

select distinct case when pt.object_type = 'INDEX' then ai.table_owner
    else pt.object_owner end as owner,
  case when pt.object_type = 'INDEX' then ai.table_name
    else pt.object_name end as table_name
from plan_table pt
left join all_indexes ai on ai.owner = pt.object_owner
and ai.index_name = pt.object_name
where pt.object_type in ('TABLE', 'INDEX');

您还需要在每次解释计划调用和查询之前确保计划表为空以避免混淆,或者设置语句 ID 以便您可以识别与当前查询相关的表。

【讨论】:

  • 这很好 :) 它是开箱即用的想法。
  • 不错。只有一件事:如果 CBO 使用索引而不是它的表怎么办?这可能是一个问题吗?我试图使用 DBMS_SQL,但在我看来它可以提供列,而不是表
  • @Aleksej - 是的,我想知道使用 dbms_sql.parse,但看不到获取表信息的方法。关于索引的好点。唔。使用 Florin 的查询,如果我在 DHW_ACCOUNTS(ACC_SOURCE_ID, ACC_PT_KEY) 上有一个索引,那么计划显示会显示它对该索引进行范围扫描并且没有说它命中了表;但我的查询仍然显示两个表。
  • @Aleksej - 不,那不是真的;我仍然有以前的查询计划。如果object_type 是索引,您可以加入all_indexes 以获取表名。
  • 最终,一个伟大而优雅的解决方案。谢谢
猜你喜欢
  • 2010-10-24
  • 2021-11-15
  • 2014-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-24
  • 2010-10-01
  • 1970-01-01
相关资源
最近更新 更多