Skip to Main Content

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Kerna.

Asked: March 16, 2023 - 7:43 pm UTC

Last updated: May 10, 2023 - 4:38 am UTC

Version: Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production Version 19.18.0.0.0

Viewed 10K+ times! This question is

You Asked


Hi

First time asking a question here, I have tried to follow the guidelines, apologies for any errors on my part.

Any help you can give will be gratefully received, even if it's just to point me in the right direction rather than an answer

First a bit of context. We are in the process of removing technical debt and as such we performing a migration of data from customised tables back to the original base tables for the application where possible.

As an interim measure, we are migrating the data and replacing the custom tables with views which point to the new storage locations of the data. The view synonyms have the same names as the custom tables that they replace so that downstream systems don't fall over. Once all interfaces and other dependent code have been migrated to use the new locations, these interim views will be dropped.

One of these views has to take data which is now in a clob and produce a view which chunks the clob into 240 character strings to match how the data used to be stored producing

level text
1 text string first 240
2 text string next 240
3 text string another 240
.
.
.

I used the following select to produce that output (and I need the surrogate ID from tab_a)

select  a_surrogate_id,
        v.lvl,
        v.chnk, 
  from  tab_a,
        (select subj, crse, term, to_char(regexp_substr(s, '.{240}', 1, lvl)) chnk, lvl
         from (select subj, crse,  term, s, level lvl
               from (select b_subj_code subj, b_crse_numb crse, b_valid_from term, substr(b_required_materials,1,4000) s from tab_b) 
         connect by level <= length(s) / 240) ) v
  where  a_subj_code = v.subj
  and    a_crse_numb = v.crse
  and    v.term = (select max(sy.b_valid_from)
                   from   tab_b sy
                   where  sy.b_subj_code = v.subj
                   and    sy.b_crse_numb = v.crse
                   and    sy.b_valid_from <= a_valid_from) 


which works, but is really, really slow (add an infinite number of reallys) and when I do a select from the view with a where clause, I have time to run a marathon before it comes back (and I run slowly).

The explain plan is the following

------------------------------------------------------------------------------------------
| Id  | Operation                            | Name              | Rows  | Bytes | Cost  |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |                   |     1 | 16470 | 34307 |
|   1 |  NESTED LOOPS                        |                   |   815 |    12M| 14222 |
|   2 |   VIEW                               |                   | 22813 |   357M|   532 |
|   3 |    CONNECT BY WITHOUT FILTERING      |                   |       |       |       |
|   4 |     TABLE ACCESS FULL                | tab_B             | 22813 |    11M|   532 |
|   5 |   TABLE ACCESS BY INDEX ROWID BATCHED| tab_A             |     1 |    37 |     1 |
|   6 |    INDEX RANGE SCAN                  | A_KEY_INDEX       |     1 |       |     1 |
|   7 |     SORT AGGREGATE                   |                   |     1 |    17 |       |
|   8 |      FIRST ROW                       |                   |     1 |    17 |     1 |
|   9 |       INDEX RANGE SCAN (MIN/MAX)     | UNIQUE_B          |     1 |    17 |     1 |
------------------------------------------------------------------------------------------



If I add a where clause to this bit

select subj, crse, term, to_char(regexp_substr(s, '.{240}', 1, lvl)) chnk, lvl
         from (select subj, crse,  term, s, level lvl
               from (select b_subj_code subj, b_crse_numb crse, b_valid_from term, substr(b_required_materials,1,4000) s from tab_b <b>where clause</b>) 
         connect by level <= length(s) / 240) 


------------------------------------------------------------------------------------
| Id  | Operation                             | Name       | Rows  | Bytes | Cost  |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |            |     1 | 16433 |     1 |
|   1 |  VIEW                                 |            |     1 | 16433 |     1 |
|   2 |   CONNECT BY WITHOUT FILTERING        |            |       |       |       |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED| tab_B      |     1 |   522 |     1 |
|   4 |     INDEX RANGE SCAN                  | UNIQUE_B   |     1 |       |     1 |
------------------------------------------------------------------------------------


and it returns in a flash, because it can use the indexes and no full table scan.

So, my questions are

- is there a better way of writing the view other than the regexp_substr ? I don't know how long the clob is so I discarded the infinite number of select union select union select union.... possibility. In the example I get the first 4000 characters but in reality it could be more.
- if there isn't, is there a way of avoiding the full table scan?

and Chris said...

The full scan is only part of the problem!

The CONNECT BY LEVEL clause links every other row in the table to the hierarchy!

Here's an example with a ten-row table that should be split into five rows each:

create table t (
  c1 int, c2 varchar2(1000)
);

insert into t 
with rws as (
  select level x from dual
  connect by level <= 10
)
  select x, 
         rpad ( 'line 1', 10, '1' ) ||
         rpad ( ' line 2', 10, '2' ) ||
         rpad ( ' line 3', 10, '3' ) ||
         rpad ( ' line 4', 10, '4' ) ||
         rpad ( ' line 5', 10, '5' ) 
  from   rws;

select count(*) from t
connect by level <= length ( c2 ) / 10;

  COUNT(*)
----------
    111110


The query returns over 100k rows!

Look at a few rows and you'll see it combines row one with the other rows:

select connect_by_root c1 rt, level dpth, 
       sys_connect_by_path ( c1, '-' ) pth, 
       t.*
from   t
connect by level <= length ( c2 ) / 10
fetch  first 10 rows only;

        RT       DPTH PTH                C1 C2                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
---------- ---------- ---------- ---------- ----------------------------------------
         1          1 -1                  1 line 11111 line 2222 line 3333 line 4444 line 5555                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
         1          2 -1-1                1 line 11111 line 2222 line 3333 line 4444 line 5555                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
         1          3 -1-1-1              1 line 11111 line 2222 line 3333 line 4444 line 5555                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
         1          4 -1-1-1-1            1 line 11111 line 2222 line 3333 line 4444 line 5555                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
         1          5 -1-1-1-1-1          1 line 11111 line 2222 line 3333 line 4444 line 5555                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
         1          5 -1-1-1-1-2          2 line 11111 line 2222 line 3333 line 4444 line 5555                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
         1          5 -1-1-1-1-3          3 line 11111 line 2222 line 3333 line 4444 line 5555                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
         1          5 -1-1-1-1-4          4 line 11111 line 2222 line 3333 line 4444 line 5555                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
         1          5 -1-1-1-1-5          5 line 11111 line 2222 line 3333 line 4444 line 5555                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
         1          5 -1-1-1-1-6          6 line 11111 line 2222 line 3333 line 4444 line 5555


To avoid this, add this clause to connect by:

( prior c1 = c1 or prior sys_guid() is null )


This ensures it only builds the tree from rows with the same value - this should be the primary key/unique constraint columns. The sys_guid() call is to add unique information to each row to prevent loops.

Do this and we get the 50 rows we expect:

select count(*) from t
connect by level <= length ( c2 ) / 10
and     ( prior c1 = c1 or prior sys_guid() is null );

  COUNT(*)
----------
        50


As you're on an up-to-date version of 19c, you could improve this further using SQL macros! In effect, these enable you to create parameterized views.

So you can create a function that accepts the filter criteria for TAB_B and applies this in the START WITH clause of the CONNECT BY. Use START WITH instead of WHERE because this filters the data before creating the tree. Using WHERE it'll generate the data for every row in the table, then filter to those rows you want.

Here's a quick example:

create or replace function split_rows ( 
  tab_id integer 
) return clob sql_macro as
  stmt clob;
begin
  stmt := q'[
    select * from t
    start with c1 = tab_id  
    connect by level <= length ( c2 ) / 10
    and     ( prior c1 = c1 or prior sys_guid() is null )
  ]';
  
  return stmt;
end;
/

select * from split_rows ( 1 );
/*
        C1 C2                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
---------- -----------------------------------------------------------
         1 line 11111 line 2222 line 3333 line 4444 line 5555                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
         1 line 11111 line 2222 line 3333 line 4444 line 5555                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
         1 line 11111 line 2222 line 3333 line 4444 line 5555                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
         1 line 11111 line 2222 line 3333 line 4444 line 5555                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
         1 line 11111 line 2222 line 3333 line 4444 line 5555    
*/   


You'll need to update the code to pass the necessary parameters, so this may be something to consider later in the migration.

Rating

  (4 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Kerna, March 24, 2023 - 12:09 am UTC

Thank you!

Can't believe I missed the join !

I added the join and it was still incredibly slow, but I ended up reading up all about sql macros so it was still worth it.

I ended up getting rid of regexp_substr and going with dbms_lob.substr as in the example below:

SELECT DBMS_LOB.SUBSTR( t.comments, 240, l.COLUMN_VALUE )
FROM table_name t
CROSS JOIN
TABLE(
CAST(
MULTISET(
SELECT LEVEL * 240 - 239
FROM DUAL
CONNECT BY LEVEL * 240 - 239 <= DBMS_LOB.GETLENGTH( t.comments )
) AS SYS.ODCINUMBERLIST
)
) l
Chris Saxon
March 24, 2023 - 11:04 am UTC

Glad this helped.

no more cast/multiset in the latest generation database.

Rajeshwaran Jeyabal, March 24, 2023 - 1:54 pm UTC

But since you are in the latest generation database, you dont need that cast/multiset stuff any more
demo@PDB1> select * from t;

X
________
Hello

demo@PDB1> select substr( x,column_value,1)
  2  from t cross join
  3  table( cast( multiset( select level r
  4     from dual
  5     connect by level <= length(x) )
  6*    as sys.odcinumberlist ) ) ;

SUBSTR(X,COLUMN_VALUE,1)
___________________________
H
e
l
l
o

Instead make use of "lateral" clause like this
demo@PDB1> select substr(x,r,1)
  2  from t , lateral( select level r
  3         from dual
  4         connect by level <= length(x) )
  5* /

SUBSTR(X,R,1)
________________
H
e
l
l
o

Chris Saxon
March 27, 2023 - 1:15 pm UTC

Good point

thank you!

A reader, March 27, 2023 - 2:22 pm UTC

I will update my view this afternoon

Oracle cpu spinning with getrusage and times call

Andy, May 03, 2023 - 1:49 am UTC

My oracle process serving requests keeps spinning cpu at ~100%. this is reducing sql response time significantly as most of the time it is doing frequent getrusage and times call:

1683075608.245493 getrusage(RUSAGE_SELF, {ru_utime={8772, 382792}, ru_stime={21, 240324}, ...}) = 0 <0.000019>
1683075608.245543 getrusage(RUSAGE_SELF, {ru_utime={8772, 382792}, ru_stime={21, 240350}, ...}) = 0 <0.000019>
1683075608.245605 getrusage(RUSAGE_SELF, {ru_utime={8772, 382792}, ru_stime={21, 240371}, ...}) = 0 <0.000019>
1683075608.245661 getrusage(RUSAGE_SELF, {ru_utime={8772, 382792}, ru_stime={21, 240397}, ...}) = 0 <0.000019>
1683075608.245706 getrusage(RUSAGE_SELF, {ru_utime={8772, 382792}, ru_stime={21, 240419}, ...}) = 0 <0.000020>


1683075608.289765 times({tms_utime=877242, tms_stime=2124, tms_cutime=0, tms_cstime=0}) = 2006434122 <0.000019>
1683075608.289832 times({tms_utime=877242, tms_stime=2124, tms_cutime=0, tms_cstime=0}) = 2006434122 <0.000019>
1683075608.289880 getrusage(RUSAGE_SELF, {ru_utime={8772, 425981}, ru_stime={21, 241020}, ...}) = 0 <0.000019>
1683075608.289927 getrusage(RUSAGE_SELF, {ru_utime={8772, 425981}, ru_stime={21, 241044}, ...}) = 0 <0.000019>
1683075608.289978 times({tms_utime=877242, tms_stime=2124, tms_cutime=0, tms_cstime=0}) = 2006434122 <0.000020>

Oracle version is 11g. I can't find any meaningful hits on web as how i can stop this constant invocation on cpu metrics by oracle thread. Appreciate quick help. Many thx!
Connor McDonald
May 10, 2023 - 4:38 am UTC

Some patch levels of 11g had an issue in this area.

See MOS note 27338049.8 for details on a patch, but ... 11g? Its time to upgrade

More to Explore

SQL

The Oracle documentation contains a complete SQL reference.