3

I have a column in a table with simple sql queries and I want to regexp_substr the table name from them. Examples of texts:

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

or

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

So, I need to find the "FROM", skip some whitespaces then get the first word. I'm not interested for the moment in the second table.

What I've done:

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

Output:

 FROM DWH_OWNER.DWH_ACCOUNTS
FROM   
    DWH_OWNER.DWH_QRM_PRODUCT_TYPES

What's wrong with my qyery: It outputs the tablename with the "FROM " before the table name. I want the table name directly, without whitespaces.

Desired output:

DWH_OWNER.DWH_ACCOUNTS
DWH_OWNER.DWH_QRM_PRODUCT_TYPES

EDIT: I managed to do regexp_substr(tablename, '(\w|_|\.)+', 1,2) over the previous step to do the tablename clean. But It is possible to get the desired output with a single regexp?

Here should be an sqlfiddle, but the site does not work in this moment. The query:

  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;
Florin Ghita
  • 17,525
  • 6
  • 57
  • 76

1 Answers1

8

Rather than try to write your own parser, you could let Oracle parse it for you via explain plan, and then look at the plan table to see which objects it refers to:

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                   

As @Aleksej suggested, if the optimiser only uses an index (so the execution plan shows index access/scan without hitting the table, because all the relevant columns are in the index) then the plan table only reports the index. You could allow for that by joining to the index view; if it hits the table too it'll just report it for both:

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');

You also need to make sure the plan table is empty before each explain plan call and query to avoid confusion, or set a statement ID so you can identify which tables related to the current query.

Alex Poole
  • 183,384
  • 11
  • 179
  • 318
  • This is nice :) it's out of the box thinking. – Florin Ghita Feb 09 '16 at 16:33
  • Nice. Just one thing: what if the CBO would use an index instead of its table? could this be a problem or not?. I was trying to use DBMS_SQL, but it seems to me that it can give the columns, not the tables – Aleksej Feb 09 '16 at 16:43
  • @Aleksej - yes, I wondered about using dbms_sql.parse, but couldn't see a way to get the table info. Good point about the indexes. Hmm. Playing with Florin's query, if I have an index on `DHW_ACCOUNTS(ACC_SOURCE_ID, ACC_PT_KEY)` then plan display shows it doing a range scan on that index and doesn't say it hits the table; but my query still shows both tables. – Alex Poole Feb 09 '16 at 16:46
  • @Aleksej - no, that's not true; I still had the previous query's plan in there. You could join to `all_indexes` to get the table name though, if the `object_type` is index. – Alex Poole Feb 09 '16 at 16:55
  • In the end, a great, elegant solution. Thanks – Aleksej Feb 09 '16 at 18:29
  • It's a little bit embarrassing to admit I posted an almost [xy problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) – Florin Ghita Feb 10 '16 at 07:35