Should one revisit existing code that uses inline
Ganesh, June      28, 2002 - 12:41 pm UTC
 
 
Thanks for the prompt response. 
The plans being different for the two queries, do you think one should replace all of the existing code using inline with sql-with or tune it on a case by case basis .
Is it even worth it?
Thanks,
Ganesh. 
 
June      28, 2002 - 2:00 pm UTC 
 
I tune where tuning is necessary.  It is not worthwhile (in my opinion) to revisit every line of code everytime a new feature comes out. 
 
 
 
Differences between views, inline views and subquery factoring
andrew, June      28, 2002 - 3:17 pm UTC
 
 
Tom
As you've shown, inline views and "subquery factoring" are different. Please could you point out differences between inline views, views and "subquery factoring". Automatic query re-writes is one area of uncertainty to me. 
 
June      28, 2002 - 5:16 pm UTC 
 
There is very little difference between an inline view and a view view.  A view view is simply a stored query. An inline view just has you put the query right there.  I would say an inline view and a view view are the same for all intents and purposes.
This page describes the benefit of a WITH clause (subquery factoring).  It is a way to tell the optimizer "use this result twice, it is the same"
 
 
 
 
Can I use this in a ctas?
A reader, January   08, 2003 - 5:08 pm UTC
 
 
Can the With clause be used in a ctas? 
 
January   08, 2003 - 6:32 pm UTC 
 
ops$tkyte@ORA920.US.ORACLE.COM> drop table t;
Table dropped.
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create table t as
  2  with
  3  free_space_by_tablespace as
  4       ( select sum(bytes)/1024 Kbytes_free,
  5                            max(bytes)/1024 largest,
  6                            tablespace_name
  7             from  sys.dba_free_space
  8             group by tablespace_name ),
  9  space_by_permanent_tablespace as
 10       ( select sum(bytes)/1024 Kbytes_alloc,
 11                            sum(maxbytes)/1024 Kbytes_max,
 12                            tablespace_name
 13             from sys.dba_data_files
 14             group by tablespace_name ),
 15  space_by_temporary_tablespace as
 16       ( select sum(bytes)/1024 Kbytes_alloc,
 17                            sum(maxbytes)/1024 Kbytes_max,
 18                            tablespace_name
 19             from sys.dba_temp_files
 20             group by tablespace_name )
 21  /* Now, finally to the query itself */
 22  select b.tablespace_name name,
 23             kbytes_alloc kbytes,
 24             kbytes_alloc-nvl(kbytes_free,0) used,
 25             nvl(kbytes_free,0) free,
 26             ((kbytes_alloc-nvl(kbytes_free,0))/
 27                                                    kbytes_alloc)*100 pct_used,
 28             nvl(largest,0) largest,
 29             nvl(kbytes_max,kbytes_alloc) Max_Size,
 30             decode( kbytes_max, 0, 0, (kbytes_alloc/kbytes_max)*100) pct_max_used
 31    from free_space_by_tablespace a
 32             RIGHT OUTER JOIN
 33                space_by_permanent_tablespace b on a.tablespace_name = b.tablespace_name
 34  union all
 35  select b.tablespace_name,
 36             kbytes_alloc kbytes,
 37             kbytes_alloc-nvl(kbytes_free,0) used,
 38             nvl(kbytes_free,0) free,
 39             ((kbytes_alloc-nvl(kbytes_free,0))/
 40                                                    kbytes_alloc)*100 pct_used,
 41             nvl(largest,0) largest,
 42             nvl(kbytes_max,kbytes_alloc) Max_Size,
 43             decode( kbytes_max, 0, 0, (kbytes_alloc/kbytes_max)*100) pct_max_used
 44    from free_space_by_tablespace a
 45             RIGHT OUTER JOIN
 46                space_by_temporary_tablespace b on a.tablespace_name = b.tablespace_name
 47  /
Table created.
ops$tkyte@ORA920.US.ORACLE.COM>
 
 
 
 
 
immature oracle designers
harakiri, January   08, 2003 - 11:48 pm UTC
 
 
First, the plan in 9i is no longer a simple hierarchical query. The correct output is still produced by utlxpls, of course. All homegrown plan queries are rendered invalid with new temporary table transformation. 
The plan contains:
TABLE ACCESS FULL      SYS_TEMP_0FD9D6600_338EED1D
in 2 places. A temporary table build under the node:
RECURSIVE EXECUTION         SYS_LE_2_0
There is no match in the names whatsoever! Oracle probably don't have a clue that execution plan in the new design is essentially a Directed Acyclic Graph (DAG). DAG still can be represented as adjacency list with parent_id referring to the parent row without need to invent kludgy names and link by the other columns. I must be naive to expect a simple design from oracle.
Second, what is new manageabilty stuff with all those advisory views? Am I supposed to learn more internal details? I was assuming that oracle would remove all the unnecessary levels of implementation details, straighten up the code, and automatically size up all those internal caches and areas. Instead, I have to read more documentation! Isn't oracle just throwing more features and call it manageability? Typical oracle: simplicity, via adding more complexity! 
 
 
 
January   09, 2003 - 7:23 am UTC 
 
what the heck are you talking about and how does this at all relate to subquery factoring and the with clause???  
I have no clue what you are talking about.  the plan table is still the plan table is still the plan table.
You have no idea the complexity of the database apparently either.  
I don't get it with the advisory comment either.  Lets see, you fire up OEM, OEM tells you graphically and textually "hey, you know what, if you sized the buffer cache to X, the shared pool to Y -- things would be better.  Would you like me to do that for you?".  You press button and wah-lah, done.  
Tell me, how did you learn about directed acyclic graphs (might big words).  I'm sure you didn't invent them -- I'll bet you READ about them.  You know, in some document somewhere?  
It would really be frustrating don't you think if you sized the buffer cache to M and the shared pool to N and came in the next day to find that the database set it to X and Y without telling you?  
Remember the line "Open the pod bay doors Hal, I'm sorry Dave, I'm afraid I can't do that"
</code>  
http://www.moviesounds.com/2001/imsorry.wav  <code>
not sure I want my database reconfiguring itself at 2pm during peak load all by itself -- do you...
If you have a real question about "how do i", and want to ask it, feel free to submit it some day.    
 
 
 
plan diff
mikito, January   09, 2003 - 12:53 pm UTC
 
 
Run utlxpls and compare the output with the output you printed in the reply above. There is a whole subtree missing:
|   1 |   RECURSIVE EXECUTION                 | SYS_LE_2_0                   |       |       |       |
|   0 |    INSERT STATEMENT                   |                              |    83 |  2490 |   135 |
|   1 |     LOAD AS SELECT                    |                              |       |       |       |
|   2 |      SORT GROUP BY                    |                              |    83 |  2490 |   135 |
|   3 |       VIEW                            |                              |    83 |  2490 |   133 |
|   4 |        UNION-ALL                      |                              |       |       |       |
|   5 |         NESTED LOOPS                  |                              |    82 |  9922 |    99 |
|   6 |          NESTED LOOPS                 |                              |    82 |  7790 |    99 |
|*  7 |           TABLE ACCESS FULL           | TS$                          |    82 |  4592 |    17 |
|*  8 |           TABLE ACCESS CLUSTER        | FET$                         |     1 |    39 |     1 |
|*  9 |          INDEX UNIQUE SCAN            | I_FILE2                      |     1 |    26 |       |
|  10 |         NESTED LOOPS                  |                              |     1 |   147 |    34 |
|  11 |          NESTED LOOPS                 |                              |     1 |   121 |    34 |
|* 12 |           TABLE ACCESS FULL           | TS$                          |     1 |    82 |    17 |
|* 13 |           FIXED TABLE FIXED INDEX     | X$KTFBFE (ind:1)             |     1 |    39 |       |
|* 14 |          INDEX UNIQUE SCAN            | I_FILE2                      |     1 |    26 |       | 
Now the technical question. What is this funny name SYS_LE_2_0. How does this relate to 'SYS_TEMP_0FD9D660A_26900'? 
 
January   09, 2003 - 2:14 pm UTC 
 
Oh -- it's you.  The guy who wants the software to do all of our thinking for us.  Now I understand the aggressive, somewhat beligerent and superior tone better.
never mind.
(seems funny, you know what to do, you just want to complain about it.  oh well.)
The sys_ names are of course just system generated names -- like you would see for constraints that are not named.  there need not be any implied relation between the two you see.  They are just temporary names for temporary objects -- their names are not meaningful. 
 
 
 
diffreence between inline view and WHERE clause
A reader, January   09, 2003 - 5:03 pm UTC
 
 
Hi
since this topic is about inline views difference with other clauses I have a doubt
if I have a query like
select ename, dname
from emp, dept
where emp.deptno = dept.deptno
and b.deptno = 10
and emp.empno between 7500 and 7600
what would it differ from
select ename, dname
from (select * from emp where empno between 7500 and 7600) a,
     (select * from dept where deptno = 10) b
considering emp.empno, emp.deptno and dept.deptno are indexed?
May be this example we wont see much difference since it's only two small tables but with joins of 4 or 5 tables will changing WHERE clause in Inline views change dramatically the execution plan? 
 
January   09, 2003 - 7:04 pm UTC 
 
It can -- it might not.  I can give examples where it will, and some where it won't.  Especially when outer joins are involved (they it almost certainly WILL change the plan and the results).
In the case like above where the two queries are semantically equivalent, it'll most likely not change anything. 
 
 
 
Smaller Query?
Martin, February  27, 2003 - 9:27 am UTC
 
 
Hi Tom,
don't worry this is not a question about directed acyclic graphs ( I have enough trouble spelling it, never mind what it is! ;-)). 
You're example above of how subquery factoring can tell the optimiser to reuse the relevant parts of the subquery is great, but was wondering if you could do a quick example of a much simpler query which can benefit from the WITH clause? Every example I've tried seems to make no benefit in plan / stats. 
Thanks in advance 
 
February  27, 2003 - 7:09 pm UTC 
 
what you need is a relatively expensive subquery, used many times.  Consider:
big_table@ORA920> set echo on
big_table@ORA920> set autotrace on statistics
big_table@ORA920>
big_table@ORA920> with owners
  2  as
  3  ( select distinct owner username from all_objects )
  4  select count(*)
  5    from all_objects, owners
  6   where all_objects.owner = owners.username
  7   union all
  8  select count(*)
  9    from dba_objects, owners
 10   where dba_objects.owner = owners.username
 11  /
  COUNT(*)
----------
     30537
     30977
2 rows selected.
Statistics
----------------------------------------------------------
          4  recursive calls
          7  db block gets
     302441  consistent gets
          1  physical reads
        520  redo size
        418  bytes sent via SQL*Net to client
        499  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          3  sorts (memory)
          0  sorts (disk)
          2  rows processed
big_table@ORA920>
big_table@ORA920> select count(*)
  2    from all_objects, (select distinct owner username from all_objects ) owners
  3   where all_objects.owner = owners.username
  4   union all
  5  select count(*)
  6    from dba_objects, (select distinct owner username from all_objects ) owners
  7   where dba_objects.owner = owners.username
  8  /
  COUNT(*)
----------
     30537
     30977
2 rows selected.
Statistics
----------------------------------------------------------
         28  recursive calls
          0  db block gets
     442058  consistent gets
          0  physical reads
          0  redo size
        418  bytes sent via SQL*Net to client
        499  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          4  sorts (memory)
          0  sorts (disk)
          2  rows processed
big_table@ORA920> set autotrace off
big_table@ORA920>
See the 75%++ reduction in LIO's -- we didn't have to distinct owner from all_users two times. 
 
 
 
Hmmm..
Martin, February  28, 2003 - 3:16 am UTC
 
 
Hi Tom,
thanks for the response, but I can't seem to replicate the savings you show, i.e.
SQL> ed
Wrote file afiedt.buf
  1  with owners
  2  as
  3  ( select distinct owner username from all_objects )
  4  select count(*)
  5  from all_objects, owners
  6  where all_objects.owner = owners.username
  7  union all
  8  select count(*)
  9  from dba_objects, owners
 10* where dba_objects.owner = owners.username
SQL> /
  COUNT(*)
----------
     39911
     40088
2 rows selected.
Execution Plan
----------------------------------------------------------
ERROR:
ORA-01039: insufficient privileges on underlying objects of the view
SP2-0612: Error generating AUTOTRACE EXPLAIN report
Statistics
----------------------------------------------------------
        521  recursive calls
          8  db block gets
     798808  consistent gets
        261  physical reads
          0  redo size
        426  bytes sent via SQL*Net to client
        503  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
         18  sorts (memory)
          1  sorts (disk)
          2  rows processed
SQL> select count(*)
  2    2    from all_objects, (select distinct owner username from all_objects ) 
  3  owners
  4    3   where all_objects.owner = owners.username
  5    4   union all
  6    5  select count(*)
  7    6    from dba_objects, (select distinct owner username from all_objects ) 
  8  owners
  9    7   where dba_objects.owner = owners.username
 10  
SQL> ed
Wrote file afiedt.buf
  1  select count(*)
  2  from all_objects, (select distinct owner username from all_objects )
  3  owners
  4  where all_objects.owner = owners.username
  5  union all
  6  select count(*)
  7  from dba_objects, (select distinct owner username from all_objects )
  8  owners
  9* where dba_objects.owner = owners.username
SQL> /
  COUNT(*)
----------
     39911
     40088
2 rows selected.
Execution Plan
----------------------------------------------------------
ERROR:
ORA-01039: insufficient privileges on underlying objects of the view
SP2-0612: Error generating AUTOTRACE EXPLAIN report
Statistics
----------------------------------------------------------
         28  recursive calls
          6  db block gets
     798699  consistent gets
        255  physical reads
          0  redo size
        426  bytes sent via SQL*Net to client
        503  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          3  sorts (memory)
          1  sorts (disk)
          2  rows processed
I think this is the reason why I'm having difficulty finding situations where using the WITH works better, in the
above situation it seems worse!
 
 
 
February  28, 2003 - 9:46 am UTC 
 
It'll vary by version -- I'll bet  you have 9iR1 perhaps?  Here is another test from 9iR2 that shows the differences in the plans (i ran the queries 2 times to avoid the recursive sql counts for parsing which accounts for the nomimal differences you see above perhaps?)
ps$tkyte@ORA920> create table t1 as select * from all_objects;
Table created.
ops$tkyte@ORA920> create table t2 as select * from dba_objects;
Table created.
ops$tkyte@ORA920>
ops$tkyte@ORA920> analyze table t1 compute statistics
  2  for table for all indexes for all indexed columns;
Table analyzed.
ops$tkyte@ORA920> analyze table t2 compute statistics
  2  for table for all indexes for all indexed columns;
Table analyzed.
ops$tkyte@ORA920>
ops$tkyte@ORA920> with owners
  2  as
  3  ( select distinct owner username from t1 )
  4  select count(*)
  5  from t1, owners
  6  where t1.owner = owners.username
  7  union all
  8  select count(*)
  9  from t2, owners
 10  where t2.owner = owners.username
 11  /
  COUNT(*)
----------
     29648
     30089
ops$tkyte@ORA920> set autotrace on
ops$tkyte@ORA920> /
  COUNT(*)
----------
     29648
     30089
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=138 Card=2 Bytes=68)<b>
   1    2     RECURSIVE EXECUTION OF 'SYS_LE_2_0'
   2    0   TEMP TABLE TRANSFORMATION</b>
   3    2     UNION-ALL
   4    3       SORT (AGGREGATE)
   5    4         HASH JOIN (Cost=68 Card=948736 Bytes=32257024)
   6    5           TABLE ACCESS (FULL) OF 'T1' (Cost=42 Card=29648 Bytes=504016)
   7    5           VIEW (Cost=8 Card=29648 Bytes=504016)
   8    7             TABLE ACCESS (FULL) OF 'SYS_TEMP_0FD9D6603_F54CC05A' (Cost=8 Card=29648 Bytes=504016)
   9    3       SORT (AGGREGATE)
  10    9         HASH JOIN (Cost=70 Card=962848 Bytes=32736832)
  11   10           VIEW (Cost=8 Card=29648 Bytes=504016)
  12   11             TABLE ACCESS (FULL) OF 'SYS_TEMP_0FD9D6603_F54CC05A' (Cost=8 Card=29648 Bytes=504016)
  13   10           TABLE ACCESS (FULL) OF 'T2' (Cost=43 Card=30089 Bytes=511513)
Statistics
----------------------------------------------------------
          4  recursive calls
          7  db block gets
       1239  consistent gets
          1  physical reads
        528  redo size
        418  bytes sent via SQL*Net to client
        499  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          2  rows processed
ops$tkyte@ORA920> set autotrace off
ops$tkyte@ORA920>
ops$tkyte@ORA920> select count(*)
  2  from t1, (select distinct owner username from t1 ) owners
  3  where t1.owner = owners.username
  4  union all
  5  select count(*)
  6  from t2, (select distinct owner username from t1 ) owners
  7  where t2.owner = owners.username
  8  /
  COUNT(*)
----------
     29648
     30089
ops$tkyte@ORA920> set autotrace on
ops$tkyte@ORA920> /
  COUNT(*)
----------
     29648
     30089
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=430 Card=2 Bytes=68)
   1    0   UNION-ALL
   2    1     SORT (AGGREGATE)
   3    2       HASH JOIN (Cost=214 Card=29648 Bytes=1008032)
   4    3         TABLE ACCESS (FULL) OF 'T1' (Cost=42 Card=29648 Bytes=504016)
   5    3         VIEW (Cost=154 Card=29648 Bytes=504016)
   6    5           SORT (UNIQUE) (Cost=154 Card=29648 Bytes=504016)
   7    6             TABLE ACCESS (FULL) OF 'T1' (Cost=42 Card=29648 Bytes=504016)
   8    1     SORT (AGGREGATE)
   9    8       HASH JOIN (Cost=216 Card=30089 Bytes=1023026)
  10    9         VIEW (Cost=154 Card=29648 Bytes=504016)
  11   10           SORT (UNIQUE) (Cost=154 Card=29648 Bytes=504016)
  12   11             TABLE ACCESS (FULL) OF 'T1' (Cost=42 Card=29648 Bytes=504016)
  13    9         TABLE ACCESS (FULL) OF 'T2' (Cost=43 Card=30089 Bytes=511513)
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       1639  consistent gets
          0  physical reads
          0  redo size
        418  bytes sent via SQL*Net to client
        499  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
          2  rows processed
ops$tkyte@ORA920> set autotrace off
<b>when I did this in 9iR1 -- it was a bit different as the optimizer chose:
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=3517 Card=2 Bytes=68)
   1    0   UNION-ALL
   2    1     SORT (AGGREGATE)
   3    2       MERGE JOIN (Cost=1754 Card=22196 Bytes=754664)
   4    3         SORT (JOIN) (Cost=877 Card=22196 Bytes=377332)
   5    4           VIEW (Cost=877 Card=22196 Bytes=377332)
   6    5             SORT (UNIQUE) (Cost=877 Card=22196 Bytes=377332)
   7    6               TABLE ACCESS (FULL) OF 'T1' (Cost=23 Card=22196 Bytes=377332)
   8    3         SORT (JOIN) (Cost=877 Card=22196 Bytes=377332)
   9    8           TABLE ACCESS (FULL) OF 'T1' (Cost=23 Card=22196 Bytes=377332)
  10    1     SORT (AGGREGATE)
  11   10       MERGE JOIN (Cost=1763 Card=22273 Bytes=757282)
  12   11         SORT (JOIN) (Cost=877 Card=22196 Bytes=377332)
  13   12           VIEW (Cost=877 Card=22196 Bytes=377332)
  14   13             SORT (UNIQUE) (Cost=877 Card=22196 Bytes=377332)
  15   14               TABLE ACCESS (FULL) OF 'T1' (Cost=23 Card=22196 Bytes=377332)
  16   11         SORT (JOIN) (Cost=887 Card=22273 Bytes=378641)
  17   16           TABLE ACCESS (FULL) OF 'T2' (Cost=23 Card=22273 Bytes=378641)
for both plans</b>
different init.ora settings and such will affect the outcome as well. 
 
 
 
 
Error with with in SQQPLUS 
A Reader, February  28, 2003 - 9:28 am UTC
 
 
Hi Tom,
When I run one of the examples in SQLPLUS, I get the following error:
SQL> with owners
SP2-0734: unknown command beginning "with owner..." - rest of line ignored.
SQL>    as
SP2-0042: unknown command "as" - rest of line ignored.
SQL>    ( select distinct owner username from all_objects )
When I copy the whole statment into buffer, I get the following:
SP2-0642: SQL*Plus internal error state 2091, context 0:0:0
Unsafe to proceed
I am running 9.2 Standard Edition.  Does this version support the "with"?
Thank you.
 
 
 
February  28, 2003 - 10:14 am UTC 
 
yes it does however you are using 8i or before sqlplus ;)
SQLPlus is the guy having heartburn with "with" -- see it is an SP error, not an ORA error. 
 
 
 
Aha!
Martin, February  28, 2003 - 10:11 am UTC
 
 
Yep, in 9.2 it does seem to be significantly better, and I get the kind of savings that you show, yet in 9.1 it's worse. Oh well, another excuse (as if we needed any) to go to 9.2.
Thanks for all your help.  
 
February  28, 2003 - 10:37 am UTC 
 
Well, it is not "worse" -- just not "any better in some cases".  Here is the tkprof from 9iR1 for the original example.  I chopped the plans out cause they are huge -- but -- they were the same.
with owners
as
( select distinct owner username from all_objects )
select count(*)
from all_objects, owners
where all_objects.owner = owners.username
union all
select count(*)
from dba_objects, owners
where dba_objects.owner = owners.username
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.14       0.13          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2     14.68      15.28          0     411570          0           2
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4     14.82      15.41          0     411570          0           2
select count(*)
from all_objects, (select distinct owner username from all_objects ) owners
where all_objects.owner = owners.username
union all
select count(*)
from dba_objects, (select distinct owner username from all_objects ) owners
where dba_objects.owner = owners.username
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.10       0.10          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2     14.30      15.10          0     411570          0           2
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4     14.40      15.21          0     411570          0           2
 
 
 
 
As an addendum...
Martin, February  28, 2003 - 10:15 am UTC
 
 
Actually, Tom, further quick question, since the plans for the WITH (and WITHout..) at 9.1 are the same, does this mean we can really think of the WITH statement (at 9.1) as shorthand for the major query? "Under the covers" is this what oracle's doing?
Thanks  
 
February  28, 2003 - 10:49 am UTC 
 
Not really - it all depends on the query.  Consider this now (9iR1):
ops$tkyte@ORA9I> set autotrace on statistics
ops$tkyte@ORA9I> with owners
  2  as
  3  ( select distinct owner username from all_objects where rownum >= 1)
  4  select count(*)
  5  from all_objects, owners
  6  where all_objects.owner = owners.username
  7  union all
  8  select count(*)
  9  from dba_objects, owners
 10  where dba_objects.owner = owners.username
 11  /
  COUNT(*)
----------
     22197
     22273
Statistics
----------------------------------------------------------
          4  recursive calls
         10  db block gets
     280407  consistent gets
          1  physical reads
        516  redo size
        426  bytes sent via SQL*Net to client
        503  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          3  sorts (memory)
          0  sorts (disk)
          2  rows processed
ops$tkyte@ORA9I> select count(*)
  2  from all_objects, (select distinct owner username from all_objects where rownum >= 1) owners
  3  where all_objects.owner = owners.username
  4  union all
  5  select count(*)
  6  from dba_objects, (select distinct owner username from all_objects where rownum >= 1) owners
  7  where dba_objects.owner = owners.username
  8  /
  COUNT(*)
----------
     22197
     22273
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
     411570  consistent gets
          0  physical reads
          0  redo size
        426  bytes sent via SQL*Net to client
        503  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          4  sorts (memory)
          0  sorts (disk)
          2  rows processed
ops$tkyte@ORA9I> set autotrace off
In 9iR1 is seems that if the optimizer can merge the subquery -- it will.  Adding rownum (aggregates, order bys, set operations like intersect and such) precludes a simple merge so it materialized the subquery once off to the side and reused it twice in the first query.  
In the second query -- it did it twice. 
 
 
 
 
potential to use the same sub query twice
A reader, July      06, 2003 - 9:41 am UTC
 
 
ops$tkyte@ORA9I> set autotrace on statistics
ops$tkyte@ORA9I> with owners
  2  as
  3  ( select distinct owner username from all_objects where rownum >= 1)
  4  select count(*)
  5  from all_objects, owners
  6  where all_objects.owner = owners.username
  7  union all
  8  select count(*)
  9  from dba_objects, owners
 10  where dba_objects.owner = owners.username
 11  /
Tom in the above , can you explain.. where is the potential to to use ( select distinct owner username from all_objects where rownum >= 1) , twice?
   
 
July      06, 2003 - 4:29 pm UTC 
 
we are joining two different views -- ALL_OBJECTS and DBA_OBJECTS to this factored subquery -- so we use it twice.
Lines 5 and 9 
 
 
 
In PL/SQL?
Kashif, July      11, 2003 - 12:05 pm UTC
 
 
Hi Tom,
Can the with clause be used inside pl/sql? Unfortunately I don't have access to 9i so I'm not able to play around with this feature. I couldn't figure it out from the New Features and Warehousing guides, and I couldn't even find an explanation for the WITH clause in the 9i SQL Reference. 
Secondly, are the developers of the CBO not allowed to discuss any potential features and improvements they have included in a new release of the CBO? It seems like we're frequently guessing what the optimizer does from one release to the next, and observing radical differences in a feature in two different releases, without having any advance knowledge or expectation of the difference. Or am I not reading some document somewhere? Many thanks.
Kashif 
 
 
OK
Catherine, February  25, 2004 - 8:11 am UTC
 
 
Dear Tom,
Can the following inline view be transformed into a
correlated subquery?
sql> select deptno,max_sal,(select ename from emp where
        sal = max_sal and rownum = 1),min_sal,(select ename
     from emp where sal = min_sal and rownum = 1)
      from(
            select deptno,max(sal) as max_sal,min(sal) as
              min_sal from emp group by deptno
           );
Could you please help?
Thanks in advance. 
 
February  25, 2004 - 9:30 am UTC 
 
I do not see the relationship between
a) inline view -- the equivalent of a view or table to be selected from
b) a correlated subquery -- something you use in a predicate to restrict rows.
Your query looks a bit funky.  The question it seems to be answering is:
generate the max sals by DEPTNO
report back a random ename that makes that sal by deptno, even if that ename doesn't work in that DEPT!!!
consider:
ops$tkyte@ORA920PC> create table emp ( deptno number, sal number, ename varchar2(20) );
 
Table created.
 
ops$tkyte@ORA920PC>
ops$tkyte@ORA920PC> insert into emp values ( 10, 100, 'first row' );
 
1 row created.
 
ops$tkyte@ORA920PC> insert into emp values ( 10, 50, 'second row' );
 
1 row created.
 
ops$tkyte@ORA920PC> insert into emp values ( 20, 100, 'third row' );
 
1 row created.
 
ops$tkyte@ORA920PC> insert into emp values ( 20, 50, 'fourth row' );
 
1 row created.
 
ops$tkyte@ORA920PC>
ops$tkyte@ORA920PC> select deptno,
  2         max_sal,(select ename from emp where sal = max_sal and rownum = 1),
  3         min_sal,(select ename from emp where sal = min_sal and rownum = 1)
  4     from( select deptno,max(sal) as max_sal,min(sal) as
  5           min_sal from emp group by deptno
  6           );
 
    DEPTNO    MAX_SAL (SELECTENAMEFROMEMPW    MIN_SAL (SELECTENAMEFROMEMPW
---------- ---------- -------------------- ---------- --------------------
        10        100 first row                    50 second row
        20        100 first row                    50 second row
 
<b> is that what you really wanted?  or is this what you meant:</b>
ops$tkyte@ORA920PC>
ops$tkyte@ORA920PC> select distinct deptno,
  2         first_value(sal) over (partition by deptno order by sal, rowid) min_sal,
  3         first_value(ename) over (partition by deptno order by sal, rowid) min_ename,
  4         first_value(sal) over (partition by deptno order by sal DESC, rowid) max_sal,
  5         first_value(ename) over (partition by deptno order by sal DESC, rowid) max_ename
  6    from emp
  7  /
 
    DEPTNO    MIN_SAL MIN_ENAME               MAX_SAL MAX_ENAME
---------- ---------- -------------------- ---------- --------------------
        10         50 second row                  100 first row
        20         50 fourth row                  100 third row
 
 
 
 
 
 
WITH and complex types
vll, February  25, 2004 - 9:46 am UTC
 
 
Hi, Tom!
Great thanks as usual for all you are doing for us!!!
I'm using WITH a lot in my new projects and it really helps. But recently I tried to include in WITH query some complex types (mostly - XMLTYPE fields) from one of my tables. And I never had a chance to make it working - it just freezes and after a minute or 2 gives an error "lost communication with a server" or someting like this. It's 9i release 2. All my tests just tell me that with simple types WITH is working fine, as soon as you have complex type - you have a problem. Are you by chance aware of any problem with this? I can post my query, but it's really large and I don't think it can help...  
 
February  25, 2004 - 9:47 am UTC 
 
if you have an example that returns with the 3113 - -please file a tar with support, that would be a "bug" 
 
 
 
I'm getting the same error
Steve, February  26, 2004 - 8:02 pm UTC
 
 
I'm also getting the 3113 error (on 9.2.0.4), but it's happening inconsistently.  For a while, the error was coming up about 50% of the time.  A few hours later, it was less than 10%.  When the error doesn't happen, I find no problems with the results of the process.
Briefly, my process is running a packaged procedure which passes a PL/SQL collection (varray) to a remote (dblink) packaged procedure which creates a flat file for that data using UTL_FILE.  In the procedure that builds the PL/SQL collection, several other collections (each is a SQL table type) are used to pass data between packaged functions.  These collections are being used in SQL statements using "TABLE( CAST(" in the functions.
One of the things I've had a problem with is deciding where to define the PL/SQL types (remote site or local site).  Both options have resulted in problems.  The current setup is defining on the remote site.
I don't know if "vll from USA" has a similar setup, but if so, I hope you will let us know if you find a solution, and I will do the same.
Tom, can you think of any hints and tips you could give us as far as the best approach for doing this kind of thing?
 
 
February  27, 2004 - 7:19 am UTC 
 
you *need* to contact support to resolve a 3113 like this.  it means "back end went away - crashed". 
 
 
 
Found a way around the 3113
Steve, February  27, 2004 - 8:54 pm UTC
 
 
It doesn't appear that anything was actually crashing, since I was using both instances in other windows which continued to work before and after the error.  Anyway, I found a workaround for the error, which I'll describe below in case anyone is doing something similar.  But first, a little soapbox action:
I was reluctant to contact Oracle Support because, in the 15+ years that I've worked on Oracle, I've gotten very little help from them.  The responses I usually get are either that I have to upgrade to a later version or that the feature I'm using wasn't meant to be used that way.  In either case, I spend hours communicating with them and still have to find a workaround.  So now I always try to find a workaround first and contact Support only as a last resort.
<getting off soapbox>
The application I'm working on is available on an internet site and allows the users to retrieve a variety of statistical data in various formats (report, chart, or CSV file).  There are two instances involved, the first (call it DB1) contains the data warehouse and the second (call it DB2) contains the internet-related data.  Each is on a separate Unix box.
The 3113 error was happening in the routines that create the CSV file.  A request is initiated on DB2 by a user.  Packaged functions and procedures are called on DB1 to build the data in a PL/SQL varray.  The varray is passed back to DB2 to build the file using utl_file so the user can download it.  Since the same type must be used on both sides to avoid a type mismatch, the type must be defined on either DB1 or DB2 and referenced via dblink by the other.
The first attempt to do this was by calling the packaged routines on DB1 which built the varray and called a procedure on DB2 passing it the varray.  When the type was defined on DB1, DB2 didn't recognize the type.  When the type was defined on DB2, the 3113 error came up intermittently.
The new variation calls a packaged procedure on DB2 which creates the varray (empty) and passes it as an IN OUT parameter to a procedure on DB1 which calls the other functions to build the varray.  When the type was defined on DB2, I wasn't able to make it work.  But, when the type was defined on DB2, everything seemed to be okay.  I've run it about 50 times so far, and it is working consistently.
Hopefully, this information will be helpful to someone out there.  If anyone would like more detail on this let me know.
Tom, thanks for all the help you provide on this site.  Sorry for not following your advice on contacting Oracle Support, but, as I said, that's not worked out very well for me in the past.  
 
February  28, 2004 - 10:30 am UTC 
 
your *session* crashed -- your dedicated server crashed.  the instance did not fail, your session did.
support is actually pretty good at 3113, ora-600's, things that create trace files.  for the "feature wasn't meant to be used like that", i cannot really respond, I'd need an example - but many times i find that to "be the case" (using shared server to run really long transactions -- yet getting upset that no one can log in since the shared servers are all currently busy.  using replication for failover/DR yet not having any conflict detection/resolution routines and getting upset when the other site comes backup and starts pushing deferred transactions.  the list is very very long)
but, anyway.
If you have a reproducible test case (and it seems you do) it would really benefit us all by filing the tar with it.  You have something concrete they can work with -- and instead of others hitting this issue in the future, they would just have it "work".  Thats my soapbox -- if you don't tell them there is a problem, problem might well as not exist and problem will never get fixed for anyone. 
 
 
 
3113 again
Steve, March     02, 2004 - 5:19 pm UTC
 
 
Good point, Tom.  I will try to put together a reproducible test case - one which is not so tightly tied to our environment, but still gets the 3113.  I need to concentrate on getting my project finished, since it is already overdue, but I should be able to work on this later in the week or early next week.  Thanks for your help. 
 
 
what is wrong here
PRS, April     15, 2004 - 1:22 pm UTC
 
 
Following query gives me ORA-904 invalid identifier. Inline view does not see table B. Any idea?
SELECT B.opportunity_id 
,B.BO_ID_CUST 
,A.WSI_ACCOUNT_ID 
,E.WSI_ACCOUNT_NO 
,B.FIRST_NAME 
,B.LAST_NAME 
,B.OPPORTUNITY_STATUS 
,f.xlatlongname status_desc
,A.TD_OPP_FLW_UP_ACT 
,g.xlatlongname acat_desc
,B.PERSON_ID 
,D.NAME 
,A.RA_CAMPAIGN_ID 
,C.RA_CMPGN_NAME 
,B.ROW_ADDED_DTTM 
,B.ROW_LASTMANT_DTTM 
,B.NEXT_ACTIVITY_DT
,B.ACT_CLOSE_DT 
,B.ACT_REVENUE 
,B.EST_REVENUE 
,B.PHONE 
,'' dummy
,h.descrlong  
,', Created By ' || h.row_added_oprid || ', on ' || h.row_added_dttm note
FROM PS_WSI_RSF_OPP_FLD A 
, PS_RSF_OPPORTUNITY B 
, PS_RA_CAMPAIGN C 
, PS_RD_PERSON_NAME D 
, PS_WSI_ACCOUNT E 
,PSXLATITEM F
,PSXLATITEM G
,(select descrlong,row_added_oprid,row_added_dttm
  from ( select descrlong,row_added_oprid,row_added_dttm
           from ps_rsf_opp_note k
          where k.opportunity_id = B.opportunity_id
          order by note_seq_nbr DESC )
 where rownum = 1)  h
WHERE A.OPPORTUNITY_ID = B.OPPORTUNITY_ID 
AND A.RA_CAMPAIGN_ID = C.RA_CAMPAIGN_ID(+) 
AND B.PERSON_ID = D.PERSON_ID(+) 
AND A.WSI_ACCOUNT_ID = E.WSI_ACCOUNT_ID(+)
AND f.fieldvalue = b.opportunity_status
AND f.fieldname = 'OPPORTUNITY_STATUS'
AND e.wsi_account_no = '33312345'
AND g.fieldvalue = A.TD_OPP_FLW_UP_ACT 
AND g.fieldname = 'TD_OPP_FLW_UP_ACT' 
 
April     15, 2004 - 2:15 pm UTC 
 
you cannot do that.  that would be joining to a correlated subquery.
 
 
 
 
inline views
raj, April     28, 2004 - 2:32 am UTC
 
 
Hi Tom,
which one of the below queries is better:
1)
select
sum(sal) sal,
sum(comm) comm,
sum(sal) + sum(comm) totalsal,
(sum(comm)/sum(sal)) * 100 percent
from
emp
group by deptno
2)
select
sal,
comm,
sal + comm totalsal,
(comm/sal) * 100 percent
(
select
sum(sal) sal,
sum(comm) commision
from
emp
group by deptno
)
in the first query, are the sum(sal), sum(comm) computed more than once because of more occurances?
Version : Oracle 9.2 
 
 
Can we force the temp table route ?
Adrian, September 01, 2004 - 11:37 am UTC
 
 
Tom,
The docs say that subquery factoring enables EITHER a temp table or an in-line view, depending on whichever Oracle deems suitable at the time. Is there a way to force the temp table as the in-line view is causing me serious trouble with additional executions of a user-function which I need to eliminate.
I want to be able to run the SQL with the user-function call INSIDE the WITH clause once, and then be able to reference the function's return value OUTSIDE the WITH clause, without the penalty of re-executing the function. At the moment, the CBO is choosing an ILV over a temp table, causing it to re-execute the function to evaluate it in the outermost predicate. This is causing much pain...
BTW, 9.2.0.5.
Regards
Adrian 
 
September 01, 2004 - 11:54 am UTC 
 
give me example query to work with please.   
 
 
 
Read consistency with WITH
Arun Gupta, September 01, 2004 - 12:43 pm UTC
 
 
Tom,
When using WITH clause, when does the point in time read consistency start? Is it with the execution of subquery in the WITH clause or when the main SELECT starts?
Thanks. 
 
September 01, 2004 - 1:24 pm UTC 
 
the query is the query -- the "whole thing" 
 
 
 
An example...
Adrian, September 02, 2004 - 7:14 am UTC
 
 
Tom,
The following is the example. As you can see, a temp table transformation is not occurring, which means that any references to the pseudo-column created from my function call are resulting in new executions. The one that causes the most trouble is the one inside the analytic function.
If I create either a GTT or fixed table of the results of the subquery, then the main select flies...
EXPLAIN PLAN FOR
WITH ilv AS (
             SELECT ss7.ss7_rec_id
             ,      dms.dms_rec_id
             ,      ss7.release_call_date
             ,      ss7.start_call_date
             ,      TRUNC(EXTRACT(SECOND FROM (ss7.release_call_date-dms.cdr_date) )) AS date_diff
             ,      ROUND(TO_NUMBER((CAST(ss7.release_call_date AS DATE)-CAST(ss7.start_call_date AS DATE))*86400),4) - NVL(dms.duration, 0) AS duration_diff
             ,      dms_tools.is_matched(
                       ss7.a_number_suffix,
                       ss7.b_number,
                       dms.a_num_suffix,
                       dms.b_num,
                       ss7.release_call_date-ss7.start_call_date,
                       dms.duration
                       ) AS match_type
             FROM   abb_ss7 ss7
             ,      abb_dms dms
             WHERE  ss7.release_call_date_int IN ( dms.cdr_date_int, dms.cdr_date_int + 1 )
            )
      SELECT ss7_rec_id
      ,      dms_rec_id
      ,      match_type
      ,      duration_diff
      ,      date_diff
      FROM  (
             SELECT ss7_rec_id
             ,      dms_rec_id
             ,      match_type
             ,      duration_diff
             ,      date_diff
             ,      ROW_NUMBER() OVER
                       ( PARTITION BY ss7_rec_id
                         ORDER BY match_type, ABS(duration_diff), ABS(date_diff) ) AS rn
             FROM  ilv
             WHERE match_type != 4
            )
      WHERE rn = 1
select * from table (dbms_xplan.display)
 
--------------------------------------------------------------------------------
| Id  | Operation                |  Name       | Rows  | Bytes |TempSpc| Cost  |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT         |             |   184K|    13M|       |   114 |
|*  1 |  VIEW                    |             |   184K|    13M|       |   114 |
|*  2 |   WINDOW SORT PUSHED RANK|             |   184K|    17M|       |   114 |
|   3 |    CONCATENATION         |             |       |       |       |       |
|*  4 |     HASH JOIN            |             |    63 |  6363 |  1008K|    56 |
|   5 |      TABLE ACCESS FULL   | ABB_SS7     | 14629 |   828K|       |    15 |
|   6 |      TABLE ACCESS FULL   | ABB_DMS     | 25231 |  1059K|       |    11 |
|*  7 |     HASH JOIN            |             |    63 |  6363 |  1008K|    56 |
|   8 |      TABLE ACCESS FULL   | ABB_SS7     | 14629 |   828K|       |    15 |
|   9 |      TABLE ACCESS FULL   | ABB_DMS     | 25231 |  1059K|       |    11 |
--------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter("from$_subquery$_003"."RN"=1)
   2 - filter(ROW_NUMBER() OVER ( PARTITION BY "SS7"."SS7_REC_ID" ORDER BY 
              "CDR"."DMS_TOOLS"."IS_MATCHED"("SS7"."A_NUMBER_SUFFIX","SS7"."B_NUMBER","DMS"."A
              _NUM_SUFFIX","DMS"."B_NUM","SS7"."RELEASE_CALL_DATE"-"SS7"."START_CALL_DATE","DM
              S"."DURATION"),ABS(ROUND(TO_NUMBER(TO_CHAR((CAST("SS7"."RELEASE_CALL_DATE" AS 
              DATE)-CAST("SS7"."START_CALL_DATE" AS DATE))*86400)),4)-NVL("DMS"."DURATION",0))
              ,ABS(TRUNC(EXTRACT(SECOND FROM "SS7"."RELEASE_CALL_DATE"-"DMS"."CDR_DATE"))))<=1
              )
   4 - access("SS7"."RELEASE_CALL_DATE_INT"="DMS"."CDR_DATE_INT"+1)
       filter("CDR"."DMS_TOOLS"."IS_MATCHED"("SS7"."A_NUMBER_SUFFIX","SS7"."B_NU
              MBER","DMS"."A_NUM_SUFFIX","DMS"."B_NUM","SS7"."RELEASE_CALL_DATE"-"SS7"."START_
              CALL_DATE","DMS"."DURATION")<>4)
   7 - access("SS7"."RELEASE_CALL_DATE_INT"="DMS"."CDR_DATE_INT")
       filter("CDR"."DMS_TOOLS"."IS_MATCHED"("SS7"."A_NUMBER_SUFFIX","SS7"."B_NU
              MBER","DMS"."A_NUM_SUFFIX","DMS"."B_NUM","SS7"."RELEASE_CALL_DATE"-"SS7"."START_
              CALL_DATE","DMS"."DURATION")<>4 AND LNNVL("SS7"."RELEASE_CALL_DATE_INT"="DMS"."C
              DR_DATE_INT"+1))
 
Note: cpu costing is off
So, short of creating a GTT and using a two-stage process, is there a way to force Oracle to use a TT transformation ?
As a test, I thought if I could make two references to the WITH subquery, then Oracle would definitely materialise the resultset. So I added a UNION ALL with a second SELECT FROM ilv WHERE 1=2 but to no avail...
Regards
Adrian 
 
September 02, 2004 - 9:34 am UTC 
 
what is the time to 
a) populate the gtt and query it
vs
b) just running the query
and why not have the match_type != 4 in the with subquery?
(although, if you add "and rownum > 0" to the subquery you might find it doing what you asked for) 
 
 
 
Major difference...
Adrian, September 02, 2004 - 10:23 am UTC
 
 
Tom,
1) 7 mins
2) 12 hours
If I add the match_type != 4 to the subquery, Oracle will re-execute the function, as verified by DBMS_XPLAN ( how did we ever manage without DBMS_XPLAN's predicate section ? ).
I will try the rownum trick...
Regards
Adrian 
 
 
How on earth... ?
Adrian, September 02, 2004 - 10:41 am UTC
 
 
Tom,
That works - DBMS_XPLAN shows TempSpc being used. How on earth does that happen with a ROWNUM > 0 predicate ? And how did you think to add it ?
BTW, the timings I gave before were for volume tests. Using the tables in the query I sent you, the wall clock timings are:-
1) GTT, then INSERT => 38 seconds
2) WITH subquery and ROWNUM > 0 => 38 seconds
3) WITH subquery ( ILV, no TT transformation ) => well, it is still running...
I'd really like to know what is going on and how you thought of it. Also, how persistent will a technique like that be across new releases..
Regards
Adrian
 
 
September 02, 2004 - 10:54 am UTC 
 
Effective Oracle by Design -- chapter on effective sql :)
various operations, like rownum, level, set operations, some sorts, aggregations cannot be "merged"
It is quite possible that a NO_MERGE hint would do the trick as well. 
 
 
 
I'd better re-read it...
Adrian, September 02, 2004 - 11:50 am UTC
 
 
Tom,
Better re-read that section then. Didn't even think about trying to avoid a merge...
Thanks
Adrian 
 
 
Indexes on temp tables?
A reader, September 14, 2004 - 9:16 pm UTC
 
 
I have many data transformation processes that go like
create table temp1 as ....
create table temp2 as select ... from temp1,....
<create indexes on temp1,temp2>
insert into final_table select ... from temp1,temp2,...
and so on
The subquery factoring (WITH clause) feature would be a great fit here, I can just define all my temp tables using the WITH clause and Oracle will either materialize them as statement-scope gtt's or simply inline views.
If the data volumes are large and it does indeed materialize them as temp tables, what about indexes? Right now, I have carefully indexed the temp tables so as to give maximum benefit. How can I do this using the WITH feature?
Thanks 
 
September 15, 2004 - 7:37 am UTC 
 
indexes are pointless and "not the right answer" for large data sets, it would intelligently not create them, saving time, resources. 
 
 
 
A reader, September 15, 2004 - 9:31 am UTC
 
 
"indexes are pointless and "not the right answer" for large data sets, it would intelligently not create them, saving time, resources"
You are just dodging the question because I pressed the wrong button! Yes, I realize that indexes are not (always) the right answer for large datasets, but as I said, for my particular need and for what I am doing in my INSERT INTO ....WITH...SELECT statement they are critical. Thats why I said that I have carefully crafted indexes that do help.
That said, is there a way for to create temporary indexes on these (potentially "materializable" temp tables) if they are determined to be useful (by the CBO, not me!)?
Thanks 
 
September 15, 2004 - 10:09 am UTC 
 
prove it
not dodging, show me. 
 
 
 
A reader, September 15, 2004 - 11:27 am UTC
 
 
I will try to come up with a test case from my current process, but dont you admit that this is possible? i.e. indexes might be useful on these intermediate temporarily materialized tables?
If I have something like
insert into new_table
with t as (select * from <join of zillion tables>)
select * from t where c1='foo';
Wouldnt the temp table 't' benefit from a index on "c1" (assuming that only 1 row in a million has the value "foo"?)
Thanks 
 
September 15, 2004 - 11:43 am UTC 
 
nope -- we'd merge the c1='foo' into the with in this case in all likelyhood. 
 
 
 
PARALLEL INSERT & WITH Subquery_factoring
Jai Lee, November  11, 2004 - 1:14 am UTC
 
 
If I explain or run following INSERT statement, server process' CPU is 100 % under 9.2.0.5.
(alter session enable parallel dml;)
INSERT /*+ APPEND PARALLEL(A 4) */ INTO TAB1 A
WITH MISU_TMP AS
     ( SELECT .. FROM MISU WHERE.. GROUP BY COL1)
SELECT /*+ USE_HASH(B C) PARALLEL(B 4) PARALEL(C 4)*/ *
FROM TAB2 B, MISU_TMP C
UNION ALL
...
IF there is no PARALLEL Hint at INSERT regardless ENABLE PARALLEL DML, it works.
Q1. INSERT WITH subquery does not work with parallel DML?
Q2. CPU 100% means optimizer cannot parse that statement? 
 
November  11, 2004 - 7:57 am UTC 
 
q1) false
q2) means something -- if this reproduces, suggest you
   a) export the affected tables with ROWS=N
   b) export the statistics on these segments with dbms_stats to a table
   c) export that statistics table
   d) list out all NON-DEFAULT init.ora settings
   e) upload that into your tar with support -- as it should allow them to 
      reproduce the issue in a couple of seconds.
 
 
 
 
force merge on inline SQL using the WITH clause
Marc Werner, November  19, 2004 - 6:04 am UTC
 
 
Tom,
why does Oracle (10g) not merge the following:
select a.* 
from (
  with t as (select 3 c1 from dual)
  select * 
    from any_tableX b join t t1
      on (b.id = t1.c1)
      ) a
plan shows a VIEW clause at top-level !
whereas :
select a.* 
from (
 select * from tableX b join
               (select 3 c1 from dual) t1
               on (b.id = t1.c1)
      ) a
is getting merged ?!
To justify the usage of the WITH clause here -
imagine we got an INLINE query, filtering 30% of an exiting table and join that query 4 times.
Regards,
Marc Werner
 
 
November  19, 2004 - 11:04 am UTC 
 
it is DUAL after all.  FAST DUAL none the less.  You cannot extrapolate what the CBO does with a 1 row fast dual select vs "filtering 30% of an existing table..."
Not sure based on your question whether you are wondering why there is a VIEW materialized or why it didn't materialize the view in the second case. 
 
 
 
merge WITH's
Marc Werner, November  23, 2004 - 12:26 pm UTC
 
 
Tom,
on top of my 1st query I asked, why Oracle doesnt merge 
that statement, because it can merge the semantically equivalent statement beneath.
Here comes an "closer to reality" - example without using dual:
create table t1
as select * from all_objects 
where rownum < 500;
analyze table t1 compute statistics;
select tmp.* from (
with l_upd as (
    select owner, max( t1.last_ddl_time) last_ddl
    from t1
    group by owner
    ) 
select u.username, l_upd3.owner from 
     all_users u join l_upd
     on ( l_upd.owner = u.username )
     join l_upd l_upd2 on ( l_upd2.owner = u.username )
     join l_upd l_upd3 on ( l_upd3.owner = u.username )
 ) tmp;
Again we get an unecessary materialization (VIEW) at one level under root in the plan !
Why is that ? How can I force that query to get merged ?
It's crucial for DWH's queries - as I got here - to get mergeed across different inline - levels , otherwise u either end up heavily writing temp or coding like :
select a - case when t2.y=1 then 2 else 3 end,
       b - case when t2.y=1 then 2 else 3 end,
       c - case when t2.y=1 then 2 else 3 end,
       ...
from t1 join t2 on ...
instead of :
select a - calc1, b - calc1, c - ...
from t1 jon ( select case when t2.y=1 then 2 else 3 end
         from t2 ) t2
...
apparently does the with clause cause the optimizer real problems in merging ?!
I am aware of the "merge-killers" :
 - rownum, group by
 - union
 - analyt. functions 
Thanks for your patience,
Regards,
Marc. 
 
November  23, 2004 - 1:41 pm UTC 
 
with subquery factoring is specifically "designed" to materialized views as a matter of fact. it is one of its goals.
(and you said you are aware of merge killers -- you have listed group by.  well you appear to have a group by)
In your "real case" you have a group by -- you cannot join and then group, you must group and then join.
and your example further -- it references l_upd many many multiple times.
do you understand what it would mean to run that query in a merged fashion???  forget about temp (temp is cheap, buy more temp space) -- that query would never complete.  How many times do you want to execute that query???
sure, could it merge?  yes.
should it merge?  absolutely not.
ops$tkyte@ORA9IR2> select tmp.*
  2    from
  3  (
  4  with l_upd
  5  as (
  6      select owner, max( t1.last_ddl_time) last_ddl
  7      from t1
  8      group by owner
  9      )
 10  select u.username, l_upd3.owner from
 11       all_users u join l_upd
 12       on ( l_upd.owner = u.username )
 13       join l_upd l_upd2 on ( l_upd2.owner = u.username )
 14       join l_upd l_upd3 on ( l_upd3.owner = u.username )
 15   ) tmp;
 
USERNAME                       OWNER
------------------------------ ------------------------------
SYS                            SYS
 
 
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   VIEW
   2    3       RECURSIVE EXECUTION OF 'SYS_LE_3_0'
   3    1     TEMP TABLE TRANSFORMATION
   4    3       MERGE JOIN
   5    4         MERGE JOIN
   6    5           SORT (JOIN)
   7    6             NESTED LOOPS
   8    7               NESTED LOOPS
   9    8                 NESTED LOOPS
  10    9                   VIEW
  11   10                     TABLE ACCESS (FULL) OF 'SYS_TEMP_0FD9D6606_5695B330'
  12    9                   TABLE ACCESS (BY INDEX ROWID) OF 'USER$'
  13   12                     INDEX (UNIQUE SCAN) OF 'I_USER1' (UNIQUE)
  14    8                 TABLE ACCESS (CLUSTER) OF 'TS$'
  15   14                   INDEX (UNIQUE SCAN) OF 'I_TS#' (NON-UNIQUE)
  16    7               TABLE ACCESS (CLUSTER) OF 'TS$'
  17   16                 INDEX (UNIQUE SCAN) OF 'I_TS#' (NON-UNIQUE)
  18    5           SORT (JOIN)
  19   18             VIEW
  20   19               TABLE ACCESS (FULL) OF 'SYS_TEMP_0FD9D6606_5695B330'
  21    4         SORT (JOIN)
  22   21           VIEW
  23   22             TABLE ACCESS (FULL) OF 'SYS_TEMP_0FD9D6606_5695B330'
 
 
 
 
Statistics
----------------------------------------------------------
          4  recursive calls
          7  db block gets
         29  consistent gets
          1  physical reads
        520  redo size
        437  bytes sent via SQL*Net to client
        499  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          4  sorts (memory)
          0  sorts (disk)
          1  rows processed
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> select tmp.*
  2    from
  3  (
  4  select u.username, l_upd3.owner from
  5       all_users u join
  6           ( select owner, max( t1.last_ddl_time) last_ddl
  7           from t1
  8          group by owner)
  9           l_upd on ( l_upd.owner = u.username )
 10       join
 11           ( select owner, max( t1.last_ddl_time) last_ddl
 12           from t1
 13          group by owner)
 14           l_upd2 on ( l_upd2.owner = u.username )
 15       join
 16           ( select owner, max( t1.last_ddl_time) last_ddl
 17           from t1
 18          group by owner)
 19           l_upd3 on ( l_upd3.owner = u.username )
 20   ) tmp;
 
USERNAME                       OWNER
------------------------------ ------------------------------
SYS                            SYS
 
 
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   MERGE JOIN
   2    1     MERGE JOIN
   3    2       VIEW
   4    3         SORT (GROUP BY)
   5    4           TABLE ACCESS (FULL) OF 'T1'
   6    2       SORT (JOIN)
   7    6         VIEW
   8    7           SORT (GROUP BY)
   9    8             NESTED LOOPS
  10    9               NESTED LOOPS
  11   10                 NESTED LOOPS
  12   11                   TABLE ACCESS (FULL) OF 'T1'
  13   11                   TABLE ACCESS (BY INDEX ROWID) OF 'USER$'
  14   13                     INDEX (UNIQUE SCAN) OF 'I_USER1' (UNIQUE)
  15   10                 TABLE ACCESS (CLUSTER) OF 'TS$'
  16   15                   INDEX (UNIQUE SCAN) OF 'I_TS#' (NON-UNIQUE)
  17    9               TABLE ACCESS (CLUSTER) OF 'TS$'
  18   17                 INDEX (UNIQUE SCAN) OF 'I_TS#' (NON-UNIQUE)
  19    1     SORT (JOIN)
  20   19       VIEW
  21   20         SORT (GROUP BY)
  22   21           TABLE ACCESS (FULL) OF 'T1'
 
 
 
 
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       2035  consistent gets
          0  physical reads
          0  redo size
        437  bytes sent via SQL*Net to client
        499  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          5  sorts (memory)
          0  sorts (disk)
          1  rows processed
 
<b>Now, which one looks better to you?  Thank goodness it materalized the result once so it could use it repeatedly.</b>
heavily using temp is sometimes consider the "price of playing the game" here, there is nothing wrong with using temp when the stuff doesn't fit in to ram -- especially with aggregates (which you in general have to get the last row before you see the FIRST row -- they are not "slow by slow" processing friendly.
 
 
 
 
 
TOP-level doesnt merge !!
A reader, November  25, 2004 - 5:45 am UTC
 
 
Tom,
sorry, you missed the point - probably I didnt explain sufficiently .
I mean the VIEW on the top level of the plan (1st statement)- you can see
the second query is merged through that top level :
SELECT TMP.* FROM (
...
    )
The WITH - statement doesnt merge at the top level even though its mergeable as the second (semantically equal) statement proves !
So why does Oracle write the end - result to temp - when its just selecting 1:1 from it right after :
0 SELECT STATEMENT ...
1 2  VIEW    <------------ no need to materialize here !!!
       ...
Regards,
Marc. 
 
November  25, 2004 - 10:11 am UTC 
 
do you have a test case showing this is "a bad thing(tm)" -- that is, that it was actually materialized and this penalized you in some fashion.
 
(the view step doesn't always mean "materialized")....
Cause, if you change it to this:
ops$tkyte@ORA9IR2> create table t1
  2  as select owner||rownum owner, last_ddl_time from all_objects
  3  /
 
Table created.
 
ops$tkyte@ORA9IR2> create table t2
  2  as
  3  select owner username from t1;
 
Table created.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> exec dbms_stats.gather_table_stats( user, 'T1' );
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA9IR2> exec dbms_stats.gather_table_stats( user, 'T2' );
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> delete from plan_table;
 
25 rows deleted.
 
ops$tkyte@ORA9IR2> explain plan for
  2  select tmp.* from (
  3  with l_upd as (
  4      select owner, max( t1.last_ddl_time) last_ddl
  5      from t1
  6      group by owner
  7      )
  8  select u.username, l_upd3.owner from
  9       t2 u join l_upd
 10       on ( l_upd.owner = u.username )
 11       join l_upd l_upd2 on ( l_upd2.owner = u.username )
 12       join l_upd l_upd3 on ( l_upd3.owner = u.username )
 13   ) tmp
 14   where username like 'SYS%'
 15  /
 
Explained.
 
ops$tkyte@ORA9IR2> select * from table(dbms_xplan.display);
 
PLAN_TABLE_OUTPUT
-----------------------------------------------
 
-----------------------------------------------------------------------
| Id  | Operation                   |  Name   | Rows  | Bytes |TempSpc| Cost  |
------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |         |   270 | 19980 |       |    51 |
|   1 |  VIEW                       |         |   270 | 19980 |       |    51 |
|   3 |   TEMP TABLE TRANSFORMATION |         |       |       |       |       |
|   2 |    RECURSIVE EXECUTION      | SYS_LE_3|       |       |       |       |
|   0 |     INSERT STATEMENT        |         | 28723 |   504K|       |   135 |
|   1 |      LOAD AS SELECT         |         |       |       |       |       |
|   2 |       SORT GROUP BY         |         | 28723 |   504K|  1592K|   135 |
|   3 |        TABLE ACCESS FULL    | T1      | 28723 |   504K|       |    17 |
|*  4 |    HASH JOIN                |         |   270 | 32670 |       |    51 |
|*  5 |     HASH JOIN               |         |   270 | 22680 |       |    38 |
|*  6 |      HASH JOIN              |         |   270 | 12690 |       |    25 |
|*  7 |       TABLE ACCESS FULL     | T2      |   270 |  2700 |       |    12 |
|*  8 |       VIEW                  |         | 28723 |  1037K|       |    12 |
|   9 |        TABLE ACCESS FULL    | SYS_TEMP| 28723 |   504K|       |    12 |
|* 10 |      VIEW                   |         | 28723 |  1037K|       |    12 |
|  11 |       TABLE ACCESS FULL     | SYS_TEMP| 28723 |   504K|       |    12 |
|* 12 |     VIEW                    |         | 28723 |  1037K|       |    12 |
|  13 |      TABLE ACCESS FULL      | SYS_TEMP| 28723 |   504K|       |    12 |
-------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   4 - access("L_UPD3"."OWNER"="U"."USERNAME")
   5 - access("L_UPD2"."OWNER"="U"."USERNAME")
   6 - access("L_UPD"."OWNER"="U"."USERNAME")
   7 - filter("U"."USERNAME" LIKE 'SYS%')
   8 - filter("L_UPD"."OWNER" LIKE 'SYS%')
  10 - filter("L_UPD2"."OWNER" LIKE 'SYS%')
  12 - filter("L_UPD3"."OWNER" LIKE 'SYS%')
 
Note: cpu costing is off
 
36 rows selected.
<b>that actually shows "it was merged", the predicate was pushed -- it was merged</b>
and the execution profile of that query with this one:
ops$tkyte@ORA9IR2> with l_upd as (
  2      select owner, max( t1.last_ddl_time) last_ddl
  3      from t1
  4      group by owner
  5      )
  6  select u.username, l_upd3.owner from
  7       t2 u join l_upd
  8       on ( l_upd.owner = u.username )
  9       join l_upd l_upd2 on ( l_upd2.owner = u.username )
 10       join l_upd l_upd3 on ( l_upd3.owner = u.username )
 11   where username like 'SYS%'
 12  /
<b>is identical, the amount of "work" is the same all around</b>
 
 
 
 
 
pushed predicate
Marc Werner, November  25, 2004 - 12:04 pm UTC
 
 
Tom,
which info from the dbms_xplan output proves it doesnt materialize the joined data ?
 - the empty field under TempSpc ?
1.)what does "pushed predicate" mean ? 
2.)would dbms_xplan also show an empty TempSpc, if Oracle would be able to materialize an inline SQL fully in memory ( this time un-mergeable , e.g.: for the statement inside the with clause ) ??
 
thanks for the helpful info .
Regards,
Marc. 
 
November  25, 2004 - 12:35 pm UTC 
 
it pushed the predicate down -- it merged the "where username like 'SYS%'" down, it really did "merge" that view totally.  the two queries were equivalent.
The tkprofs I did (but did not post here) were sufficient for me.
If you can show me a case where the performance of the two techniques are measurably different -- I'd be glad to take a look at it.
ops$tkyte@ORA9IR2> exec dbms_stats.set_table_stats( user, 'T1', numrows => 1000000000, numblks => 10000000 );
PL/SQL procedure successfully completed.
                                                                                                                 
ops$tkyte@ORA9IR2> exec dbms_stats.set_table_stats( user, 'T2', numrows => 1000000000, numblks => 10000000 );
PL/SQL procedure successfully completed.
                                                                                                                 
ops$tkyte@ORA9IR2> delete from plan_table;
21 rows deleted.
                                                                                                                 
ops$tkyte@ORA9IR2> explain plan for
  2  select tmp.* from (
  3  with l_upd as (
  4      select owner, max( t1.last_ddl_time) last_ddl
  5      from t1
  6      group by owner
  7      )
  8  select u.username, l_upd3.owner from
  9       t2 u join l_upd
 10       on ( l_upd.owner = u.username )
 11       join l_upd l_upd2 on ( l_upd2.owner = u.username )
 12       join l_upd l_upd3 on ( l_upd3.owner = u.username )
 13   ) tmp
 14   where username like 'SYS%'
 15  /
                                                                                                                 
Explained.
                                                                                                                 
ops$tkyte@ORA9IR2> select * from table( dbms_xplan.display );
                                                                                                                 
PLAN_TABLE_OUTPUT
----------------------------------------------
                                                                                                                 
-------------------------------------------------------------------------------
| Id  | Operation                   |  Name   | Rows  | Bytes |TempSpc| Cost  |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |         |  1638G|   110T|       |    70M|
|   1 |  VIEW                       |         |  1638G|   110T|       |    70M|
|   3 |   TEMP TABLE TRANSFORMATION |         |       |       |       |       |
|   2 |    RECURSIVE EXECUTION      | SYS_LE_3|       |       |       |       |
|   0 |     INSERT STATEMENT        |         |  1000M|    42G|       |  5849K|
|   1 |      LOAD AS SELECT         |         |       |       |       |       |
|   2 |       SORT GROUP BY         |         |  1000M|    42G|       |  5849K|
|   3 |        TABLE ACCESS FULL    | T1      |  1000M|    42G|       |  1517K|
|   4 |    MERGE JOIN               |         |  1638G|   220T|       |    70M|
|   5 |     MERGE JOIN              |         |    51G|  5292G|       |    58M|
|   6 |      SORT JOIN              |         |  1600M|   110G|   239G|    47M|
|*  7 |       HASH JOIN             |         |  1600M|   110G|  2336M|  7120K|
|*  8 |        TABLE ACCESS FULL    | T2      |    50M|  1764M|       |  1517K|
|*  9 |        VIEW                 |         |  1000M|    34G|       |   946K|
|  10 |         TABLE ACCESS FULL   | SYS_TEMP|  1000M|    42G|       |   946K|
|* 11 |      SORT JOIN              |         |  1000M|    34G|    82G|    11M|
|* 12 |       VIEW                  |         |  1000M|    34G|       |   946K|
|  13 |        TABLE ACCESS FULL    | SYS_TEMP|  1000M|    42G|       |   946K|
|* 14 |     SORT JOIN               |         |  1000M|    34G|    82G|    11M|
|* 15 |      VIEW                   |         |  1000M|    34G|       |   946K|
|  16 |       TABLE ACCESS FULL     | SYS_TEMP|  1000M|    42G|       |   946K|
-------------------------------------------------------------------------------
even for terabytes of stuff... 
 
 
 
 
view on top of the plan
Marc Werner, November  26, 2004 - 6:58 am UTC
 
 
but does the plan-output :
                     Rows   TempSpc  ...
---------------------------------------------
0 SELECT STATEMENT   270
1   VIEW             270
..    ...
show me if that VIEW is materialized or not or is TKPROF the only way to find out ?
the TempSpc field is empty (can I trust this information and rule --> no temp space used ??) but the additional level 1 VIEW tells me it had not merged the full statement !!
Regards,
Marc.
 
 
November  26, 2004 - 10:20 am UTC 
 
the statement was merged, the filters are proof of that -- if it were not merged, the filters would have happend *after* the 'view' step.
If you can show us a case where by this extra "view" step in a plan is the cause of some performance related issue -- we'll be glad to take a peek at it. 
 
 
 
Difference between oracle 8i and oracle 9.0
TONY, November  30, 2004 - 6:26 am UTC
 
 
Sir 
Can you please send me the major differences between oracle 8i and oracle 9.0 my email address is netbosh@rediffmail.com 
 
November  30, 2004 - 8:01 am UTC 
 
 
 
Sudipta, December  14, 2004 - 1:27 am UTC
 
 
 
 
query subfactoring
karma, January   25, 2005 - 3:39 pm UTC
 
 
If i have view with 5 union all and every select needs to join to table x then will query subfactoring help instead of joining this table x 5 times ? 
 
January   25, 2005 - 6:51 pm UTC 
 
doubtful if it is just a straighforward join.  subquery factoring is most useful when you have something like:
select * 
  from t1, (select a complex query here) X
 where <join>
union all
select * 
  from t2, (select same complex query ) X
to factor out something complex.  a straight join to a table would be dealt with "just inline" best. 
 
 
 
Explain plan
reader, January   31, 2005 - 10:28 am UTC
 
 
what exactly is 
   1    2     RECURSIVE EXECUTION OF 'SYS_LE_2_0'
   2    0   TEMP TABLE TRANSFORMATION
   
I have seen it, but not sure how to interpret it.
Thanks. 
 
January   31, 2005 - 11:30 am UTC 
 
it is part of query rewrite that involved Oracle setting up a temporary table used to hold a subresult.
see
</code>  
http://www.dbazine.com/jlewis6.html  <code>
for a nice example  
 
 
Redo Generation with Subquery factoring 
Vivek Sharma, February  18, 2005 - 5:06 am UTC
 
 
Hi Tom,
I was going through the queries and the use of WITH clause. When I checked the execution plan, it is generating a redo of around 520 bytes. Why is it so ?
Please see below :
SQL> create table t1 as select * from all_objects;
Table created.
Elapsed: 00:00:04.02
SQL> create table t2 as select * from dba_objects;
Table created.
Elapsed: 00:00:01.03
SQL> analyze table t1 compute statistics
  2  /
Table analyzed.
Elapsed: 00:00:01.08
SQL> analyze table t2 compute statistics
  2  ;
Table analyzed.
Elapsed: 00:00:01.04
  1  with owners
  2  as
  3  ( select distinct owner username from t1 )
  4  select count(*)
  5  from t1, owners
  6  where t1.owner = owners.username
  7  union all
  8  select count(*)
  9  from t2, owners
 10* where t2.owner = owners.username
SQL> /
  COUNT(*)
----------
     28609
     28988
Elapsed: 00:00:01.03
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=88 Card=2 Bytes=44)
   1    2     RECURSIVE EXECUTION OF 'SYS_LE_2_0'
   2    0   TEMP TABLE TRANSFORMATION
   3    2     UNION-ALL
   4    3       SORT (AGGREGATE)
   5    4         HASH JOIN (Cost=44 Card=28609 Bytes=629398)
   6    5           VIEW (Cost=2 Card=16 Bytes=272)
   7    6             TABLE ACCESS (FULL) OF 'SYS_TEMP_0FD9D6602_1334A
          3' (Cost=2 Card=16 Bytes=80)
   8    5           TABLE ACCESS (FULL) OF 'T1' (Cost=41 Card=28609 By
          tes=143045)
   9    3       SORT (AGGREGATE)
  10    9         HASH JOIN (Cost=44 Card=28988 Bytes=637736)
  11   10           VIEW (Cost=2 Card=16 Bytes=272)
  12   11             TABLE ACCESS (FULL) OF 'SYS_TEMP_0FD9D6602_1334A
          3' (Cost=2 Card=16 Bytes=80)
  13   10           TABLE ACCESS (FULL) OF 'T2' (Cost=41 Card=28988 By
          tes=144940)
Statistics
----------------------------------------------------------
         90  recursive calls
          9  db block gets
       1214  consistent gets
        502  physical reads
       1260  redo size
        418  bytes sent via SQL*Net to client
        499  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          2  rows processed
SQL> ed
Wrote file afiedt.buf
  1  select count(*)
  2  from t1, ( select distinct owner username from t1 ) owners
  3  where t1.owner = owners.username
  4  union all
  5  select count(*)
  6  from t2, ( select distinct owner username from t1 ) owners
  7* where t2.owner = owners.username
SQL> /
  COUNT(*)
----------
     28609
     28988
Elapsed: 00:00:01.03
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=254 Card=2 Bytes=44)
   1    0   UNION-ALL
   2    1     SORT (AGGREGATE)
   3    2       HASH JOIN (Cost=127 Card=28609 Bytes=629398)
   4    3         VIEW (Cost=85 Card=16 Bytes=272)
   5    4           SORT (UNIQUE) (Cost=85 Card=16 Bytes=80)
   6    5             TABLE ACCESS (FULL) OF 'T1' (Cost=41 Card=28609
          Bytes=143045)
   7    3         TABLE ACCESS (FULL) OF 'T1' (Cost=41 Card=28609 Byte
          s=143045)
   8    1     SORT (AGGREGATE)
   9    8       HASH JOIN (Cost=127 Card=28988 Bytes=637736)
  10    9         VIEW (Cost=85 Card=16 Bytes=272)
  11   10           SORT (UNIQUE) (Cost=85 Card=16 Bytes=80)
  12   11             TABLE ACCESS (FULL) OF 'T1' (Cost=41 Card=28609
          Bytes=143045)
  13    9         TABLE ACCESS (FULL) OF 'T2' (Cost=41 Card=28988 Byte
          s=144940)
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       1585  consistent gets
        597  physical reads
          0  redo size
        418  bytes sent via SQL*Net to client
        499  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
          2  rows processed
SQL> with owners
  2  as
  3  ( select distinct owner username from t1 )
  4  select count(*)
  5  from t1, owners
  6  where t1.owner = owners.username
  7  union all
  8  select count(*)
  9  from t2, owners
 10  where t2.owner = owners.username
 11  /
  COUNT(*)
----------
     28609
     28988
Elapsed: 00:00:01.03
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=88 Card=2 Bytes=44)
   1    2     RECURSIVE EXECUTION OF 'SYS_LE_2_0'
   2    0   TEMP TABLE TRANSFORMATION
   3    2     UNION-ALL
   4    3       SORT (AGGREGATE)
   5    4         HASH JOIN (Cost=44 Card=28609 Bytes=629398)
   6    5           VIEW (Cost=2 Card=16 Bytes=272)
   7    6             TABLE ACCESS (FULL) OF 'SYS_TEMP_0FD9D6603_1334A
          3' (Cost=2 Card=16 Bytes=80)
   8    5           TABLE ACCESS (FULL) OF 'T1' (Cost=41 Card=28609 By
          tes=143045)
   9    3       SORT (AGGREGATE)
  10    9         HASH JOIN (Cost=44 Card=28988 Bytes=637736)
  11   10           VIEW (Cost=2 Card=16 Bytes=272)
  12   11             TABLE ACCESS (FULL) OF 'SYS_TEMP_0FD9D6603_1334A
          3' (Cost=2 Card=16 Bytes=80)
  13   10           TABLE ACCESS (FULL) OF 'T2' (Cost=41 Card=28988 By
          tes=144940)
Statistics
----------------------------------------------------------
          4  recursive calls
          7  db block gets
       1198  consistent gets
        405  physical reads
        520  redo size
        418  bytes sent via SQL*Net to client
        499  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          2  rows processed
I executed WITH query several times and can see this redo generated.
Regards
Vivek Sharma
 
 
 
February  18, 2005 - 8:46 am UTC 
 
it could be because it wrote to a temp table.  
it could be block cleanouts.
it could be auditing.
it could be lots of stuff -- but hey, 520 bytes -- so?
 
 
 
 
WITH VS INLINE
VKOUL, February  28, 2005 - 2:41 pm UTC
 
 
I have changed the query to incorporate WITH clause
Statistics Of Plain Query
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        349  consistent gets
          0  physical reads
          0  redo size
       1379  bytes sent via SQL*Net to client
       6624  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
         48  sorts (memory)
          0  sorts (disk)
          1  rows processed
Statistics Of Query (WITH Clause)
Statistics
----------------------------------------------------------
          4  recursive calls
          7  db block gets
        134  consistent gets
          1  physical reads
        520  redo size
       1379  bytes sent via SQL*Net to client
       5305  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
         28  sorts (memory)
          0  sorts (disk)
          1  rows processed
Q1 : Which one should be considered better ?
Thanks
 
 
February  28, 2005 - 2:43 pm UTC 
 
looks like you referenced an inline view 2 times and cut the work in half -- #2 looks better from here. 
 
 
 
VKOUL, March     01, 2005 - 6:09 pm UTC
 
 
The above query set was run on small set of data say 50 records and it took 42 to 63 milliseconds, but when I ran it on a set of data containing 100s of 1000s of records, the #2 query just took hours & hours, and finally I had to get the sesion killed.
Query #1
SELECT 'DWFPM51'     "Report ID",
       'O'           "Report Type",
       'Q'           "Period Type",
       2004 "Program Year",
       2      "Quarter",
       DECODE(msfw, 1, 'Y', 'N') msfw,
       1,
       'TEST',
       COUNT(DISTINCT(CASE
                         WHEN NOT (rownumber = 1 AND office = 0 AND next_service_id IS NULL)
                         THEN seeker_id
                      END
                     )
            ) "Total Population",
       COUNT(DISTINCT(CASE
                         WHEN service_id = 112 AND office = 1
                         THEN seeker_id
                      END
                     )
            )  "Referred To Jobs",
       COUNT(DISTINCT(CASE
                         WHEN rownumber  != 1 -- rownumber = 2 Commented Out.
                          AND service_id != 0 -- 0 Service IDs Are From seeker_data Table.
                         THEN seeker_id
                      END
                     )
            )  "Some Reportable Service",
       COUNT(DISTINCT(CASE
                         WHEN rownumber != 1
                          AND service_id = 379
                         THEN seeker_id
                      END
                     )
            )  "Referred To Supportive Service",
       COUNT(DISTINCT(CASE
                         WHEN rownumber  != 1
                          AND service_id IN (96, 98, 100, 102, 104, 106, 114, 126, 158, 327, 328, 341, 344, 345, 347, 357, 372, 384, 385, 399, 409)
                         THEN seeker_id
                      END
                     )
            )  "Received Carrer Guidance",
       COUNT(DISTINCT(CASE
                         WHEN rownumber != 1
                          AND service_id = 358
                         THEN seeker_id
                      END
                     )
            )  "Job Development Contact Made",
       COUNT(DISTINCT(CASE
                         WHEN EXISTS (SELECT 'x'
                                      FROM   msfw_placement_iot t6
                                      WHERE  t3.seeker_id          =       t6.seeker_id
                                      AND    employment_start_date BETWEEN '01-jul-2004' AND '31-dec-2004'
                                                                  AND    office_id             = 1
                                  --  The IOT table has job_order_id NN.   AND    job_order_id          IS NOT NULL
                                     )
                         THEN seeker_id
                      END
                     )
            )  "MSFW Placed",
       COUNT(DISTINCT(CASE
                         WHEN EXISTS (SELECT 'x'
                                      FROM   msfw_placement_iot t6
                                      WHERE  t3.seeker_id          =       t6.seeker_id
                                      AND    employment_start_date BETWEEN '01-jul-2004' AND '31-dec-2004'
                                  --  The IOT table has job_order_id NN.   AND    job_order_id          IS NOT NULL
                                      AND    onet_code             NOT IN ('NULL', '45-2092.00', '45-2092.02', '45-2091.00')
                                                                  AND    office_id             = 1
                                     )
                         THEN seeker_id
                      END
                     )
            )  "Long Term Non-Agriculture Job"
FROM  (SELECT t1.seeker_id, t1.initial_entry_date entry_service, t1.origin,
              (SELECT 1
               FROM  (SELECT seeker_id, ROW_NUMBER() OVER (PARTITION BY seeker_id ORDER BY maint_history_id DESC) rownumber
                      FROM   msfw_seeker_flag_iot
                      WHERE  maintenance_date <= '31-dec-2004'
                     ) msfw
               WHERE  msfw.seeker_id = t1.seeker_id
               AND    rownumber = 1
              ) msfw,
              t1.service_id,
              t1.office,
              LEAD(service_id) OVER (PARTITION BY t1.seeker_id ORDER BY t1.initial_entry_date, service_id) next_service_id,
              ROW_NUMBER() OVER (PARTITION BY t1.seeker_id ORDER BY t1.initial_entry_date, service_id) rownumber
       FROM  (SELECT seeker_id, initial_entry_date, 'SD' origin, 0 service_id,
                     CASE
                        WHEN office_id = 1
                        THEN 1 -- Same Office.
                        ELSE 0 -- Different Office.
                     END office
              FROM   seeker_data t1
              WHERE  initial_entry_date BETWEEN '01-jul-2004' AND NVL((SELECT MIN(service_date)
                                                                    FROM  (SELECT seeker_id, service_date
                                                                           FROM  (SELECT seeker_id, service_date, office_id,
                                                                                         LEAD(office_id) OVER (PARTITION BY seeker_id ORDER BY service_date) next_office
                                                                                  FROM  (SELECT seeker_id, actual_start_date service_date, office_id
                                                                                         FROM   services_and_activities
                                                                                         WHERE  actual_start_date  BETWEEN '01-jul-2004' AND '31-dec-2004'
                                                                                         UNION  ALL
                                                                                         SELECT seeker_id, service_date                  , office_id
                                                                                         FROM   seeker_services
                                                                                         WHERE  service_date       BETWEEN '01-jul-2004' AND '31-dec-2004'
                                                                                        )
                                                                                 )
                                                                           WHERE  office_id   = 1
                                                                           OR     next_office = 1
                                                                          ) t2
                                                                    WHERE  t2.seeker_id = t1.seeker_id
                                                                  ), '31-dec-2004')
              UNION  ALL
              SELECT seeker_id, service_date, origin, service_id,
                     CASE
                        WHEN office_id = 1
                        THEN 1 -- Same Office.
                        ELSE 0 -- Different Office.
                     END office
              FROM  (SELECT seeker_id, office_id, service_date, origin, service_id,
                            LEAD(office_id) OVER (PARTITION BY seeker_id ORDER BY service_date) next_office
                     FROM  (SELECT seeker_id, actual_start_date service_date, 'SA' origin , service_id, office_id
                            FROM   services_and_activities
                            WHERE  actual_start_date  BETWEEN '01-jul-2004' AND '31-dec-2004'
                            UNION  ALL
                            SELECT seeker_id, service_date                  , 'SS' origin , seeker_service_type_id, office_id
                            FROM   seeker_services
                            WHERE  service_date       BETWEEN '01-jul-2004' AND '31-dec-2004'
                           )
                    )
              WHERE  office_id   = 1
              OR     next_office = 1
             ) t1
      ) t3
GROUP  BY DECODE(msfw, 1, 'Y', 'N')
/
SQL> /
Elapsed: 00:00:29.05
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=6758 Card=1452 Bytes
          =84216)
   1    0   INDEX (RANGE SCAN) OF 'PK_MSFW_PLACEMENT_IOT' (UNIQUE) (Co
          st=2 Card=1 Bytes=35)
   2    0   INDEX (RANGE SCAN) OF 'PK_MSFW_PLACEMENT_IOT' (UNIQUE) (Co
          st=2 Card=1 Bytes=42)
   3    0   SORT (GROUP BY) (Cost=6758 Card=1452 Bytes=84216)
   4    3     VIEW (Cost=6752 Card=1452 Bytes=84216)
   5    4       WINDOW (SORT) (Cost=6752 Card=1452 Bytes=55176)
   6    5         VIEW (Cost=7178 Card=1452 Bytes=55176)
   7    6           UNION-ALL
   8    7             FILTER
   9    8               TABLE ACCESS (FULL) OF 'SEEKER_DATA' (Cost=443
          8 Card=36 Bytes=648)
  10    8               SORT (AGGREGATE)
  11   10                 VIEW (Cost=6 Card=2 Bytes=96)
  12   11                   WINDOW (SORT) (Cost=6 Card=2 Bytes=70)
  13   12                     VIEW (Cost=4 Card=2 Bytes=70)
  14   13                       UNION-ALL
  15   14                         TABLE ACCESS (BY INDEX ROWID) OF 'SE
          RVICES_AND_ACTIVITIES' (Cost=2 Card=1 Bytes=18)
  16   15                           INDEX (RANGE SCAN) OF 'SAA_IX1' (N
          ON-UNIQUE) (Cost=1 Card=4)
  17   14                         TABLE ACCESS (BY INDEX ROWID) OF 'SE
          EKER_SERVICES' (Cost=2 Card=1 Bytes=18)
  18   17                           INDEX (RANGE SCAN) OF 'SS_IX1' (NO
          N-UNIQUE) (Cost=1 Card=6)
  19    7             VIEW (Cost=2308 Card=1416 Bytes=86376)
  20   19               WINDOW (SORT) (Cost=2308 Card=1416 Bytes=67968
          )
  21   20                 VIEW (Cost=2300 Card=1416 Bytes=67968)
  22   21                   UNION-ALL
  23   22                     TABLE ACCESS (BY INDEX ROWID) OF 'SERVIC
          ES_AND_ACTIVITIES' (Cost=3 Card=350 Bytes=7700)
  24   23                       INDEX (RANGE SCAN) OF 'DW_SAA_IX2' (NO
          N-UNIQUE) (Cost=2 Card=352)
  25   22                     TABLE ACCESS (FULL) OF 'SEEKER_SERVICES'
           (Cost=2297 Card=1066 Bytes=23452)
Statistics
----------------------------------------------------------
          0  recursive calls
         56  db block gets
    1032971  consistent gets
      50112  physical reads
          0  redo size
        807  bytes sent via SQL*Net to client
       6295  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
     167757  sorts (memory)
          2  sorts (disk)
          2  rows processed
SQL> disc
Disconnected from Oracle9i Enterprise Edition Release 9.2.0.5.0 - 64bit Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.5.0 - Production
SQL> 
Query #2
SELECT 'DWFPM15'     "Report ID",
       'O'           "Report Type",
       'Q'           "Period Type",
       2004 "Program Year",
       2      "Quarter",
       DECODE(msfw, 1, 'Y', 'N') msfw,
       1,
       'TEST',
       COUNT(DISTINCT(CASE
                         WHEN NOT (rownumber = 1 AND office = 0 AND next_service_id IS NULL)
                         THEN seeker_id
                      END
                     )
            ) "Total Population",
       COUNT(DISTINCT(CASE
                         WHEN service_id = 112 AND office = 1
                         THEN seeker_id
                      END
                     )
            )  "Referred To Jobs",
       COUNT(DISTINCT(CASE
                         WHEN rownumber  != 1 -- rownumber = 2 Commented Out.
                          AND service_id != 0 -- 0 Service IDs Are From seeker_data Table.
                         THEN seeker_id
                      END
                     )
            )  "Some Reportable Service",
       COUNT(DISTINCT(CASE
                         WHEN rownumber != 1
                          AND service_id = 379
                         THEN seeker_id
                      END
                     )
            )  "Referred To Supportive Service",
       COUNT(DISTINCT(CASE
                         WHEN rownumber  != 1
                          AND service_id IN (96, 98, 100, 102, 104, 106, 114, 126, 158, 327, 328, 341, 344, 345, 347, 357, 372, 384, 385, 399, 409)
                         THEN seeker_id
                      END
                     )
            )  "Received Carrer Guidance",
       COUNT(DISTINCT(CASE
                         WHEN rownumber != 1
                          AND service_id = 358
                         THEN seeker_id
                      END
                     )
            )  "Job Development Contact Made",
       COUNT(DISTINCT(CASE
                         WHEN EXISTS (SELECT 'x'
                                      FROM   msfw_placement_iot t6
                                      WHERE  t3.seeker_id          =       t6.seeker_id
                                      AND    employment_start_date BETWEEN '01-jul-2004' AND '31-dec-2004'
                                                                  AND    office_id             = 1
                                  --  The IOT table has job_order_id NN.   AND    job_order_id          IS NOT NULL
                                     )
                         THEN seeker_id
                      END
                     )
            )  "MSFW Placed",
       COUNT(DISTINCT(CASE
                         WHEN EXISTS (SELECT 'x'
                                      FROM   msfw_placement_iot t6
                                      WHERE  t3.seeker_id          =       t6.seeker_id
                                      AND    employment_start_date BETWEEN '01-jul-2004' AND '31-dec-2004'
                                  --  The IOT table has job_order_id NN.   AND    job_order_id          IS NOT NULL
                                      AND    onet_code             NOT IN ('NULL', '45-2092.00', '45-2092.02', '45-2091.00')
                                                                  AND    office_id             = 1
                                     )
                         THEN seeker_id
                      END
                     )
            )  "Long Term Non-Agriculture Job"
FROM  (SELECT t1.seeker_id, t1.initial_entry_date entry_service, t1.origin,
              (SELECT 1
               FROM  (SELECT seeker_id, ROW_NUMBER() OVER (PARTITION BY seeker_id ORDER BY maint_history_id DESC) rownumber
                      FROM   msfw_seeker_flag_iot
                      WHERE  maintenance_date <= '31-dec-2004'
                     ) msfw
               WHERE  msfw.seeker_id = t1.seeker_id
               AND    rownumber = 1
              ) msfw,
              t1.service_id,
              t1.office,
              LEAD(service_id) OVER (PARTITION BY t1.seeker_id ORDER BY t1.initial_entry_date, service_id) next_service_id,
              ROW_NUMBER() OVER (PARTITION BY t1.seeker_id ORDER BY t1.initial_entry_date, service_id) rownumber
       FROM  (WITH
              services AS
              (SELECT seeker_id, office_id, service_date, origin, service_id,
                      LEAD(office_id) OVER (PARTITION BY seeker_id ORDER BY service_date) next_office
               FROM  (SELECT seeker_id, actual_start_date service_date, 'SA' origin , service_id, office_id
                      FROM   services_and_activities
                      WHERE  actual_start_date  BETWEEN '01-jul-2004' AND '31-dec-2004'
                      UNION  ALL
                      SELECT seeker_id, service_date                  , 'SS' origin , seeker_service_type_id, office_id
                      FROM   seeker_services
                      WHERE  service_date       BETWEEN '01-jul-2004' AND '31-dec-2004'
                     )
              ) -- Services Query Reference.
              SELECT seeker_id, initial_entry_date, 'SD' origin, 0 service_id,
                     CASE
                        WHEN office_id = 1
                        THEN 1 -- Same Office.
                        ELSE 0 -- Different Office.
                     END office
              FROM   seeker_data t1
              WHERE  initial_entry_date BETWEEN '01-jul-2004' AND NVL((SELECT MIN(service_date)
                                                                    FROM  (SELECT seeker_id, service_date
                                                                           FROM   services
                                                                           WHERE  office_id   = 1
                                                                           OR     next_office = 1
                                                                          ) t2
                                                                    WHERE  t2.seeker_id = t1.seeker_id
                                                                  ), '31-dec-2004')
              UNION  ALL
              SELECT seeker_id, service_date, origin, service_id,
                     CASE
                        WHEN office_id = 1
                        THEN 1 -- Same Office.
                        ELSE 0 -- Different Office.
                     END office
              FROM   services
              WHERE  office_id   = 1
              OR     next_office = 1
             ) t1
      ) t3
GROUP  BY DECODE(msfw, 1, 'Y', 'N')
/
SQL> /
Elapsed: 00:00:00.00
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=4453 Card=1452 Bytes
          =84216)
   1    0   INDEX (RANGE SCAN) OF 'PK_MSFW_PLACEMENT_IOT' (UNIQUE) (Co
          st=2 Card=1 Bytes=35)
   2    0   INDEX (RANGE SCAN) OF 'PK_MSFW_PLACEMENT_IOT' (UNIQUE) (Co
          st=2 Card=1 Bytes=42)
   3    0   SORT (GROUP BY) (Cost=4453 Card=1452 Bytes=84216)
   4    3     VIEW (Cost=4447 Card=1452 Bytes=84216)
   5    4       WINDOW (SORT) (Cost=4447 Card=1452 Bytes=55176)
   6    5         VIEW (Cost=4584 Card=1452 Bytes=55176)
   7    8             RECURSIVE EXECUTION OF 'SYS_LE_8_0'
   8    6           TEMP TABLE TRANSFORMATION
   9    8             UNION-ALL
  10    9               FILTER
  11   10                 TABLE ACCESS (FULL) OF 'SEEKER_DATA' (Cost=4
          438 Card=36 Bytes=648)
  12   10                 SORT (AGGREGATE)
  13   12                   VIEW (Cost=2 Card=1416 Bytes=67968)
  14   13                     TABLE ACCESS (FULL) OF 'SYS_TEMP_0FD9D66
          02_5919F12C' (Cost=2 Card=1416 Bytes=49560)
  15    9               VIEW (Cost=2 Card=1416 Bytes=86376)
  16   15                 TABLE ACCESS (FULL) OF 'SYS_TEMP_0FD9D6602_5
          919F12C' (Cost=2 Card=1416 Bytes=49560)
SQL> 
I'll post the statistics when they will be available as the query #2 is taking very long to execute.
Thanks
 
 
 
March     01, 2005 - 6:39 pm UTC 
 
don't post anything, this is too big for "review"
please don't post a query here just to be "tuned" 
 
 
 
Why ?
Rj, March     02, 2005 - 11:23 am UTC
 
 
Oracle9i Enterprise Edition Release 9.2.0.5.0 - 64bit Production
 1  with e
  2  as (select * from emp),
  3  d as (select * from dept where deptno = 400)
  4  select * from e
  5*              ,d
SQL> /
no rows selected
Why this doesn't return any rows. I haven't joined "d" to "e". So, I would expect to see the rows from emp irrespective of what "d" has.
Please explain. 
Thanks! 
 
 
March     02, 2005 - 11:35 am UTC 
 
you have joined d to e
from e, d
that is the join.
when you join N rows to 0 rows, you get zero rows.
you would have to OUTER join E to D
with e
  as (select * from emp),
  d as (select * from dept where deptno = 400)
  select * from e left outer join d on (1=1)
/
 
 
 
 
Thanks
rj, March     02, 2005 - 12:16 pm UTC
 
 
Thanks Tom! Got the point! 
 
 
VKOUL, March     03, 2005 - 11:59 am UTC
 
 
Sorry for posting big queries, you said the queries are to be tuned and I do not understand that part. The broad outline of query is
(Simple) Query #1
SELECT SUM(DECODE(col1, ...
FROM  (SELECT ...
       FROM  (SELECT ...
              FROM  (SELECT ...
                     FROM
                    )
             )
      )
(WITH) Query #2
SELECT SUM(DECODE(col1, ...
FROM  (SELECT ...
       FROM  (SELECT ...
              FROM  (WITH ...
                     ...
                    )
             )
      )
***************************************************
Query # 1 (Simple)
Elapsed: 00:00:30.09
Statistics
----------------------------------------------------------
          0  recursive calls
         56  db block gets
    1032971  consistent gets
      50112  physical reads
          0  redo size
        794  bytes sent via SQL*Net to client
       6295  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
     167757  sorts (memory)
          2  sorts (disk)
          2  rows processed
Query # 2 (WITH clause)
Elapsed: 08:54:33.02
Statistics
----------------------------------------------------------
          4  recursive calls
        111  db block gets
  128667459  consistent gets
  127151785  physical reads
        728  redo size
        807  bytes sent via SQL*Net to client
       4967  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
      83879  sorts (memory)
          2  sorts (disk)
          2  rows processed
SQL> disc
Disconnected from Oracle9i Enterprise Edition Release 9.2.0.5.0 - 64bit Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.5.0 - Production
SQL> 
I don't know why this is taking almost 9 hours to run, when the normal query takes 30 seconds to run.
Look at consistent gets and physical reads, they have exploded.
The change (To use WITH clause) is done in innermost inline view only, rest all of the query is same, fetches the same result.
Thanks 
 
 
 
Forcing the use of Temporary Tables using WITH
Drew, March     03, 2005 - 6:18 pm UTC
 
 
In the SQL Reference, it states (about subquery factoring) that: "Oracle optimizes the query by treating the query name as either an inline view or as a temporary table."
Is there any way to force/influence Oracle to considering the query as a temporary table?
I ask because running:
WITH x AS (SELECT ... FROM XXX ...)
     y AS (SELECT ... FROM YYY ...)
SELECT ...
FROM ...
WHERE a IN (SELECT ... FROM x) AND
      b IN (SELECT ... FROM y) 
...
It takes 40 seconds, and 460,000 consistent gets.
However, when I replace the queries in the WITH clause with a couple of real tables (CTAS):
CREATE TABLE x AS (SELECT ... FROM XXX ...);
CREATE TABLE y AS (SELECT ... FROM YYY ...);
and run the same query (minus the WITH clause):
SELECT ...
FROM ...
WHERE a IN (SELECT ... FROM x) AND
      b IN (SELECT ... FROM y) 
...
It now only takes 0.07 seconds, and 400 consistent gets!!
I want the benefit of creating the temporary tables (which take just about 1 second using CTAS) using subquery factoring. 
Can it be done? 
 
March     03, 2005 - 6:21 pm UTC 
 
adding rownum to the select list, or using the materialize view (not documented) should do it. 
 
 
 
Query factoring
Anil, March     04, 2005 - 6:38 am UTC
 
 
Hi Tom 
I had a query to get the minimum value from table cfr_leg  staring from table cfr_leg_seg. cfr_leg_seg_map is an association table between cfr_leg_seg and cfr_leg(IOT).
I manged to write a query as follows with normal ways
Var x number;
Exec :x := 78348;
select min(FLL.CMRCL_CGO_WT_BU),min(FLL.CMRCL_CGO_vol_BU) avcap from CFR_LEG FLL 
where fll.fll_id in (
select fls.fll_id  from CFR_LEG_SEG_MAP FLS,cfr_leg_seg cfr 
where cfr.cfr_id=fls.cfr_id and cfr.cfm_id =:X)
it generates a consistent read of 800 for processing about 700 rows from all the 3 tables..
Then I wrote the same query using with cluase that gives me the same result with 19 cr. I also tested the perforamcne and found with query is 10 times faster than a normal join
with 
cfrid as (select cfr.cfr_id from cfr_leg_seg cfr where  cfr.cfm_id =78348 and  rownum < 2),
fllid as (select fls.fll_id from CFR_LEG_SEG_MAP FLS where fls.cfr_id in (select cfr_id from cfrid)),
flt_org_dst as (select flt_num , BRD_PNT,off_pnt from cfr_leg cfr where cfr.fll_id in (select * from fllid))
select min(FLL.CMRCL_CGO_WT_BU), min(FLL.CMRCL_CGO_vol_BU)   from cfr_leg fll where (flt_num 
,brd_pnt,off_pnt) in (select flt_num , BRD_PNT,off_pnt from  flt_org_dst) 
and SCH_DEP_DATE >='01-jan-2004' and 
SCH_DEP_DATE <= '31-jul-2004';
It looks query factoring can be used for efficient access in many places.   Is my observation correct. Also is there any drawback in using with clause that I need to take care.
Your advice would be valuable
Rgds
Anil 
 
 
 
Query factoring
Anil, March     04, 2005 - 6:44 am UTC
 
 
Hi Tom 
I had a query to get the minimum value from table cfr_leg  staring from table cfr_leg_seg. cfr_leg_seg_map is an association table between cfr_leg_seg and cfr_leg(IOT).
I manged to write a query as follows with normal ways
Var x number;
Exec :x := 78348;
select min(FLL.CMRCL_CGO_WT_BU),min(FLL.CMRCL_CGO_vol_BU) avcap from CFR_LEG FLL 
where fll.fll_id in (
select fls.fll_id  from CFR_LEG_SEG_MAP FLS,cfr_leg_seg cfr 
where cfr.cfr_id=fls.cfr_id and cfr.cfm_id =78348)
and day_of_opn in (1,5,7)
  6  /
MIN(FLL.CMRCL_CGO_WT_BU)      AVCAP
------------------------ ----------
                      34          4
Elapsed: 00:00:00.03
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=238 Card=1 Bytes=38)
   1    0   SORT (AGGREGATE)
   2    1     NESTED LOOPS (Cost=238 Card=54 Bytes=2052)
   3    2       NESTED LOOPS (Cost=111 Card=127 Bytes=2667)
   4    3         INDEX (RANGE SCAN) OF 'CFR_CFM_I' (INDEX) (Cost=3 Card=106 Bytes=1060)
   5    3         INDEX (RANGE SCAN) OF 'FLS_PK' (INDEX (UNIQUE)) (Cost=2 Card=1 Bytes=11)
   6    2       TABLE ACCESS (BY INDEX ROWID) OF 'CFR_LEG' (TABLE) (Cost=1 Card=1 Bytes=17)
   7    6         INDEX (UNIQUE SCAN) OF 'FLL_PK' (INDEX (UNIQUE)) (Cost=0 Card=1)
Statistics
----------------------------------------------------------
        169  recursive calls
          0  db block gets
        923  consistent gets
          0  physical reads
          0  redo size
        467  bytes sent via SQL*Net to client
        512  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
it generates a consistent read of 800 for processing about 700 rows from all the 3 tables..
Then I wrote the same query using with cluase that gives me the same result with 19 cr. I also tested the perforamcne and found with query is 10 times faster than a normal join
with 
cfrid as (select cfr.cfr_id from cfr_leg_seg cfr where  cfr.cfm_id =78348 and  rownum < 2),
fllid as (select fls.fll_id from CFR_LEG_SEG_MAP FLS where fls.cfr_id in (select cfr_id from cfrid)),
flt_org_dst as (select flt_num , BRD_PNT,off_pnt from cfr_leg cfr where cfr.fll_id in (select * from fllid))
select min(FLL.CMRCL_CGO_WT_BU), min(FLL.CMRCL_CGO_vol_BU)   from cfr_leg fll where (flt_num 
,brd_pnt,off_pnt) in (select flt_num , BRD_PNT,off_pnt from  flt_org_dst) 
and SCH_DEP_DATE >='01-jan-2004' and 
SCH_DEP_DATE <= '31-jul-2004';
------------------------ -------------------------
                      34                         4
Elapsed: 00:00:00.01
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=15 Card=1 Bytes=75)
   1    0   SORT (AGGREGATE)
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'CFR_LEG' (TABLE) (Cost=9 Card=1 Bytes=32)
   3    2       NESTED LOOPS (Cost=15 Card=1 Bytes=75)
   4    3         NESTED LOOPS (Cost=6 Card=1 Bytes=43)
   5    4           NESTED LOOPS (Cost=5 Card=1 Bytes=24)
   6    5             VIEW (Cost=3 Card=1 Bytes=13)
   7    6               COUNT (STOPKEY)
   8    7                 INDEX (RANGE SCAN) OF 'CFR_CFM_I' (INDEX) (Cost=3 Card=106 Bytes=1060)
   9    5             INDEX (RANGE SCAN) OF 'FLS_PK' (INDEX (UNIQUE)) (Cost=2 Card=1 Bytes=11)
  10    4           TABLE ACCESS (BY INDEX ROWID) OF 'CFR_LEG' (TABLE) (Cost=1 Card=1 Bytes=19)
  11   10             INDEX (UNIQUE SCAN) OF 'FLL_PK' (INDEX (UNIQUE)) (Cost=0 Card=1)
  12    3         INDEX (RANGE SCAN) OF 'CFR_LEG_I2' (INDEX) (Cost=2 Card=1)
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         19  consistent gets
          0  physical reads
          0  redo size
        487  bytes sent via SQL*Net to client
        512  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
It looks query factoring can be used for efficient access in many places.   Is my observation correct. Also is there any drawback in using with clause that I need to take care.
Your advice would be valuable
Rgds
Anil 
 
 
March     04, 2005 - 8:24 am UTC 
 
yes, this technique can be useful.
there are drawbacks to everything -- it is a tool.  Just as you would not use "distinct" on every query or "union over union all".  Use it when it makes sense to you to use it.  Use it when you have for example a complex inline view/subquery that you reference in more than one location. 
 
 
 
Materializing Subquery
Drew, March     04, 2005 - 10:18 am UTC
 
 
Thanks so much for the info about using rownum in the SELECT clause of the subquery. It worked like a charm. It will free us from having to manuallt build, populate, then drop temporary tables in a few places.
Does the use of rownum *guarantee* the materialization of the temporary table? If not, could you show me how I can specify the undocumented "materialize view" option? 
 
 
Is one more likely to result in a materialization?
Drew, March     04, 2005 - 2:58 pm UTC
 
 
Thanks for your quick and helpful responses.
Considering the materialize hint vs. the use of rownum : is one more likely to result in materialization that the other?
When you say that the server would have to change something radical, do you mean that Oracle's behavior would have to be "coded" differently for it not to materialize when rownum or /*+ materialize */ is used? 
 
March     04, 2005 - 6:12 pm UTC 
 
Oracles current behaviour would have to change -- rownum, since it is assigned to a row as it flows out of a query block -- would cause the materialization, unless something really drastic happens (we cannot merge nor predicate push into a "view" with rownum -- as it changes the answer, it has to be "stable") 
 
 
 
Sense the sys.dba_data_files changes
Mustafa, March     24, 2005 - 9:38 am UTC
 
 
Hi Tom,
I have a question. I am beginer for using Oracle. I want to write a program which checks the tablespace size. if the table space is becoming to be full then the program have to warn me. But i don t know how can i sense changing? 
 
March     24, 2005 - 3:45 pm UTC 
 
there are billions upon billions of tools that do this (eg: this is a wheel that has been invented many times over)
why not use a system management tool like enterprise manager or anything else you want to buy?
to sense "changing", you need to keep a history.  you would need to program that yourself (or just use one of many tools out there) 
 
 
 
Temp Table Transformation
Muhammad Riaz Shahid, May       11, 2005 - 3:28 am UTC
 
 
Dear Tom!
Can you please have a look on following query and tell me why Temp Table Transformation Not happening ? (and how we can make it to use temp table transoformation ?):
WITH get_pts
as (SELECT full_name, pt_cd FROM pts)
   SELECT jh.*,<some other selects here>,
          (SELECT full_name
             FROM gt_pts
            WHERE pt_cd = jh.bl) t1,
          (SELECT full_name
             FROM gt_pts
            WHERE pt_cd = jh.b2) t2
          (SELECT full_name
             FROM gt_pts
            WHERE pt_cd = jh.b3) t3,
          (SELECT full_name
             FROM gt_pts
            WHERE pt_cd = jh.b4) t4,
          (SELECT full_name
             FROM gt_pts
            WHERE pt_cd = jh.b5) t5,
          (SELECT full_name
             FROM gt_pts
            WHERE pt_cd = jh.b6) t6,
....................................
..................................
from jh
where jh.col1=:1
and execution plan is (a part of execution plan actually, since original plan is too large to read):
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=7 Card=1 Bytes=519)
   1    0   FILTER
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'BOOKING_AGENTS' (Cost=
          1 Card=1 Bytes=25)
   3    2       INDEX (UNIQUE SCAN) OF 'BOOKING_AGENTS_PK' (UNIQUE)
   4    0   TABLE ACCESS (BY INDEX ROWID) OF 'CODES' (Cost=2 Card=1 Bytes=26)
   5    4     INDEX (UNIQUE SCAN) OF 'CODES_PK' (UNIQUE) (Cost=1 Card=1)
   6    0   SORT (GROUP BY NOSORT) (Cost=3 Card=1 Bytes=19)
   7    6     TABLE ACCESS (BY INDEX ROWID OF 'CONSOLIDATION_STATUS'
          (Cost=3 Card=1 Bytes=19)
   8    7       INDEX (RANGE SCAN) OF 'CONSOLIDATION_STATUS_PK' (UNIQUE) (Cost=2 Card=1)
   9    0   TABLE ACCESS (BY INDEX ROWID) OF 'PTS' (Cost=2 Card=1 Bytes=17)
  10    9     INDEX (UNIQUE SCAN) OF 'PTS_PK' (UNIQUE) (Cost=1 Card=1)
  11    0   TABLE ACCESS (BY INDEX ROWID) OF 'PTS' 
(Cost=2 Card=1 Bytes=17)
  12   11     INDEX (UNIQUE SCAN) OF 'PTS_PK' (UNIQUE) (Cost=1 Card=1)
  13    0   TABLE ACCESS (BY INDEX ROWID) OF 'PTS' (Cost=2 Card=1 Byes=17)
  14   13     INDEX (UNIQUE SCAN) OF 'PTS_PK' (UNIQUE) (Cost=1 Card=1)
  15    0   TABLE ACCESS (BY INDEX ROWID) OF 'PTS' (Cost=2 Card=1 Bytes=17)
  16   15     INDEX (UNIQUE SCAN) OF 'PTS_PK' (UNIQUE) (Cost=1 Card=1)
  17    0   TABLE ACCESS (BY INDEX ROWID) OF 'PTS' (Cost=2 Card=1 Bytes=17)
  18   17     INDEX (UNIQUE SCAN) OF 'PTS_PK' (UNIQUE) (Cost=1 Card=1)
  19    0   TABLE ACCESS (BY INDEX ROWID) OF 'PTS' (Cost=2 Card=1 Bytes=17)
  20   19     INDEX (UNIQUE SCAN) OF 'PTS_PK' (UNIQUE) (Cost=1 Card=1)
Thanks in Advance 
 
May       11, 2005 - 7:28 am UTC 
 
scalar subqueries are different and aren't you glad it did not.  did you really want to full scan a temporary table instead of index probing on each row.
but this looks like a pivot to me, I wouldn't even consider a join I don't think 
 
 
 
Agreed
Muhammad Riaz Shahid, May       11, 2005 - 8:17 am UTC
 
 
Ok i am agreed. i have one more question. consider the case (just a simple example):
SQL> ed
Wrote file afiedt.buf
  1  create view test as
  2  with aa as (Select sum(sal) sm from scott.emp)
  3  select deptno,dname, (select * From aa) sm_Sal
  4* from scott.dept
SQL> /
View created.
SQL> select * From test;
    DEPTNO DNAME              SM_SAL
---------- -------------- ----------
        10 ACCOUNTING          18118
        20 RESEARCH            18118
        30 SALES               18118
        40 OPERATIONS          18118
SQL> ed
Wrote file afiedt.buf
  1* select distinct deptno From test
SQL> /
select distinct deptno From test
                            *
ERROR at line 1:
ORA-32035: unreferenced query name defined in WITH clause
Why so ? 
 
 
May       11, 2005 - 8:55 am UTC 
 
please contact support for that one, looks like that should be filed as a bug. 
 
 
 
cause of unreferenced error
Andrew, June      24, 2005 - 1:31 pm UTC
 
 
Question 1:
Is this correct? :
The view:
create view test as
 with aa as (Select sum(sal) sm from scott.emp)
 select deptno,dname, (select * From aa) sm_Sal
 from scott.dept
only needs to reference aa when sm_sal is selected 
 
as in
 select * From test;
The query
 select distinct deptno From test;
would be transformed to:
with aa as (Select sum(sal) sm from scott.emp)
 select distinct deptno
 from scott.dept
which will generate to the  ORA-32035 error
question 2:
This example seems to cause the temp table transformation 
by referencing the 'with' definition twice in the query.  Are there any rules to force the transformation to occur with hints, or some other usage pattern? 
 
 
June      24, 2005 - 6:41 pm UTC 
 
I didn't follow question 1?  what was the question?
2) you cannot "force" it, no 
 
 
 
cause of unreferenced error
Andrew, June      24, 2005 - 1:35 pm UTC
 
 
sorry I forgot my example:
SQL> WITH
  2   all_quarters AS -- provides start and end dates for calander qtrs from approx 2002 to 2016
  3    (SELECT DISTINCT TRUNC(SYSDATE - ii, 'Q') qtr_start_date,
  4                     TRUNC(TRUNC(sysdate - ii,'Q')+92,'Q')-1 qtr_end_date
  5      FROM (SELECT ROWNUM - 4000 ii
  6             FROM (SELECT LEVEL L
  7                    FROM DUAL CONNECT BY LEVEL < 4990
  8                  )
  9           )
 10    ),
 11   emp_sched_chg_quarters AS
 12    (SELECT DISTINCT a.employee_id employee_id,
 13                     b.qtr_end_date change_qtr
 14      FROM  ind_production_adjust_schedule a,
 15            all_quarters b
 16      WHERE  a.sale_date_begin > b.qtr_start_date AND
 17            a.sale_date_end   < b.qtr_end_date   AND
 18            a.as_of_date      = to_date('5/31/2004','mm/dd/yyyy')
 19    )
 20    SELECT c.employee_id, c.change_qtr
 21     FROM emp_sched_chg_quarters c;
EMPLOY CHANGE_QTR
------ --------------------
123456 31-MAR-2004 00:00:00
234567 30-JUN-2004 00:00:00
345678 31-MAR-2004 00:00:00
Elapsed: 00:00:45.09
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=13 Card=1 Bytes=11)
   1    0   VIEW (Cost=13 Card=1 Bytes=11)
   2    1     SORT (UNIQUE) (Cost=13 Card=1 Bytes=40)
   3    2       NESTED LOOPS (Cost=3 Card=1 Bytes=40)
   4    3         VIEW (Cost=1 Card=1 Bytes=13)
   5    4           COUNT
   6    5             VIEW (Cost=1 Card=1)
   7    6               CONNECT BY (WITHOUT FILTERING)
   8    7                 TABLE ACCESS (FULL) OF 'DUAL' (Cost=1 Card=1
          )
   9    3         TABLE ACCESS (FULL) OF 'IND_PRODUCTION_ADJUST_SCHEDU
          LE' (Cost=2 Card=1 Bytes=27)
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      94794  consistent gets
          0  physical reads
          0  redo size
        307  bytes sent via SQL*Net to client
        344  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
          3  rows processed
SQL>    --where exists (select * from all_quarters);
SQL>
SQL>
SQL>
SQL>
SQL> WITH
  2   all_quarters AS -- provides start and end dates for calander qtrs from approx 2002 to 2016
  3    (SELECT DISTINCT TRUNC(SYSDATE - ii, 'Q') qtr_start_date,
  4                     TRUNC(TRUNC(sysdate - ii,'Q')+92,'Q')-1 qtr_end_date
  5      FROM (SELECT ROWNUM - 4000 ii
  6             FROM (SELECT LEVEL L
  7                    FROM DUAL CONNECT BY LEVEL < 4990
  8                  )
  9           )
 10    ),
 11   emp_sched_chg_quarters AS
 12    (SELECT DISTINCT a.employee_id employee_id,
 13                     b.qtr_end_date change_qtr
 14      FROM  ind_production_adjust_schedule a,
 15            all_quarters b
 16      WHERE  a.sale_date_begin > b.qtr_start_date AND
 17            a.sale_date_end   < b.qtr_end_date   AND
 18            a.as_of_date      = to_date('5/31/2004','mm/dd/yyyy')
 19    )
 20    SELECT c.employee_id, c.change_qtr
 21     FROM emp_sched_chg_quarters c
 22     WHERE EXISTS (SELECT 'x' FROM all_quarters WHERE ROWNUM < 2);
EMPLOY CHANGE_QTR
------ --------------------
123456 31-MAR-2004 00:00:00
234567 30-JUN-2004 00:00:00
345678 31-MAR-2004 00:00:00
Elapsed: 00:00:01.05
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=13 Card=1 Bytes=11)
   1    2     RECURSIVE EXECUTION OF 'SYS_LE_2_0'
   2    0   TEMP TABLE TRANSFORMATION
   3    2     FILTER
   4    3       VIEW (Cost=13 Card=1 Bytes=11)
   5    4         SORT (UNIQUE) (Cost=13 Card=1 Bytes=39)
   6    5           NESTED LOOPS (Cost=3 Card=1 Bytes=39)
   7    6             VIEW (Cost=1 Card=1 Bytes=12)
   8    7               TABLE ACCESS (FULL) OF 'SYS_TEMP_0FD9D699C_894
          6E06C' (Cost=1 Card=1 Bytes=13)
   9    6             TABLE ACCESS (FULL) OF 'IND_PRODUCTION_ADJUST_SC
          HEDULE' (Cost=2 Card=1 Bytes=27)
  10    3       COUNT (STOPKEY)
  11   10         VIEW (Cost=1 Card=1)
  12   11           TABLE ACCESS (FULL) OF 'SYS_TEMP_0FD9D699C_8946E06
          C' (Cost=1 Card=1 Bytes=13)
Statistics
----------------------------------------------------------
         98  recursive calls
          9  db block gets
       1071  consistent gets
          1  physical reads
       1268  redo size
        306  bytes sent via SQL*Net to client
        344  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          3  sorts (memory)
          0  sorts (disk)
          3  rows processed
SQL>
 
 
 
 
cause of unreferenced error
Andrew, June      27, 2005 - 12:59 am UTC
 
 
Tom,
Sorry I was not clear, my first question was whether my reasoning was correct - that when this view is queried and the column defined by the 'with' is not selected.  the final transformed query will still contain the 'with' definition - thus the 'unreferenced' error.  is this correct?
Sorry my second question did not contain the example that - from testing - I believed forces the temp table transformation - whenever 2 or more references to the with definition are used.  
Since then I've found that my '2 references rule' is not a rule at all.  Sometimes it does, sometimes it doesn't.  It would be nice if you could force execution one way or another or at least know the rules the optimizer uses.
Thanks  
 
 
June      27, 2005 - 7:04 am UTC 
 
if you "with" a subquery and do not use the "withed" subquery, that is an error, yes.
forces is too strong a word, it chose to materialize the withed query in that example, yes.
The optimizers jobs it to develop the best plan for a given query, it has many choices and things it can do.
 
 
 
 
In PL/SQL?
Dan, July      28, 2005 - 4:34 pm UTC
 
 
Can WITH be used anything like this:
open refcur
 for
with t as (select * from user_objects)
select ...
or
with t as (select * from user_objects)
open refcur
for
select ...
Thanks 
 
July      29, 2005 - 7:32 am UTC 
 
the with is "part of SQL", it would go after the for
ops$tkyte@ORA9IR2> variable x refcursor
ops$tkyte@ORA9IR2> exec open :x for with data as (select * from dual) select * from data;
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA9IR2> print x
 
D
-
X
 
ops$tkyte@ORA9IR2>
 
 
 
 
 
ROWNUM >0 in Factored Subquery
Paul Carpenter, September 05, 2005 - 12:10 pm UTC
 
 
I was having BIG problems trying to get a factored subquery to be materialized, added the MATERIALIZED hint to no avail, but tucked away in this post was the "ROWNUM > 0" may help advice, and low and behold it highlighted the fact that my main query was selecting the ROWID from one of the factored tables, which obviously prevents the optimizer from being able to use a temporary table and so simply used the factored subquery as shorthand and always re-executed. Adding the "ROWNUM > 0 clause caused a ORA-1446 - cannot select ROWID from a view" which led me to the path of enlightenment. Once I'd aliased the ROWID column in the subquery and used that instead in the main query everything worked a treat no need to use the hint
Keep up the good work. 
 
 
materialize hint
Andrew, October   11, 2005 - 11:03 am UTC
 
 
i have not seen a query where this hint does not force the temp transformation
> with a as (select /*+ materialize */ 'x' from dual)
  2  select * from a;
'
-
x
Elapsed: 00:00:01.00
Execution Plan
----------------------------------------------------------
          0
SELECT STATEMENT Optimizer=CHOOSE
          1                  2
    RECURSIVE EXECUTION OF 'SYS_LE_2_0'
          2                  0
  TEMP TABLE TRANSFORMATION
          3                  2
    VIEW
          4                  3
      TABLE ACCESS (FULL) OF 'SYS_TEMP_0FD9D6601_D33E6ED5'
 
 
 
cpu cost for complex XML to relational query returns high figures
Flavio Casetta, November  03, 2005 - 11:54 am UTC
 
 
Hello Tom,
I have a query like this:
select /* EXTRACT_ORDERS */
       extractvalue(value(g), '/InvOrder/Nr') as InvOrderNr,
       extractvalue(value(g), '/InvOrder/Item') as Item,
       extractvalue(value(g), '/InvOrder/Qty') as Qty,
       extractvalue(value(g), '/InvOrder/Priority') as Priority,
       extractvalue(value(g), '/InvOrder/Reordering') as Reordering,
       extractvalue(value(g), '/InvOrder/HlOrder') as HLOrder,
       extractvalue(value(g), '/InvOrder/DelDate') as DelDate,
       f.cust,
       f.mod,
       f.tod,
       f.seq,
       f.pickwavenr,
       f.pickwaveProdline,
       f.pickwavetour,
       f.pickwavetourseq,
       f.prodrunnr,
       f.prodrundate,
       f.prodrunfinal
  from (select value(e) as customernode,
               extractvalue(value(e), '/Customer/Cust') as Cust,
               extractvalue(value(e), '/Customer/Mod') as Mod,
               extractvalue(value(e), '/Customer/Tod') as Tod,
               extractvalue(value(e), '/Customer/Seq') as Seq,
               d.pickwavenr,
               d.pickwaveProdline,
               d.pickwavetour,
               d.pickwavetourseq,
               d.prodrunnr,
               d.prodrundate,
               d.prodrunfinal
          from (select value(c) as pickwavenode,
                       extractvalue(value(c), '/PickWave/Nr') as PickWaveNr,
                       extractvalue(value(c), '/PickWave/ProdLine') as PickWaveProdLine,
                       extractvalue(value(c), '/PickWave/Tour') as PickWaveTour,
                       extractvalue(value(c), '/PickWave/TourSeq') as PickWaveTourSeq,
                       a.prodrunnr,
                       a.prodrundate,
                       a.prodrunfinal
                  from (select value(b) as ProdRunNode,
                               extractvalue(value(b), '/ProdRun/Nr') as ProdRunNr,
                               to_date(extractvalue(value(b), '/ProdRun/Date'),'DDMMYYYY') as ProdRunDate,
                               extractvalue(value(b), '/ProdRun/Final') as ProdRunFinal
                          from xml_orders x,
                               table(xmlsequence(extract(x.xmlfile,'/PickData/ProdRun'))) b
                         where x.id = 15
                       ) a,
                       table(xmlsequence(extract(a.prodrunnode,'/ProdRun/PickWave'))) c
               ) d,
               table(xmlsequence(extract(d.pickwavenode,'/PickWave/Customer'))) e
       ) f,
       table(xmlsequence(extract(f.customernode,'/Customer/InvOrder'))) g;
whose plan is calculated as follows:
Plan hash value: 3430051198
-------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |               |       |       |    14T(100)|          |
|   1 |  NESTED LOOPS                         |               |  4451T|   130P|    14T  (1)|49498625:1|
|   2 |   NESTED LOOPS                        |               |   544G|    15T|  1818M  (1)|6060:04:01|
|   3 |    NESTED LOOPS                       |               |    66M|  1845M|   222K  (1)| 00:44:31 |
|   4 |     NESTED LOOPS                      |               |  8168 |   215K|    30   (0)| 00:00:01 |
|   5 |      TABLE ACCESS BY INDEX ROWID      | XML_ORDERS    |     1 |    25 |     1   (0)| 00:00:01 |
|*  6 |       INDEX UNIQUE SCAN               | XML_ORDERS_PK |     1 |       |     0   (0)|          |
|   7 |      COLLECTION ITERATOR PICKLER FETCH|               |       |       |            |          |
|   8 |     COLLECTION ITERATOR PICKLER FETCH |               |       |       |            |          |
|   9 |    COLLECTION ITERATOR PICKLER FETCH  |               |       |       |            |          |
|  10 |   COLLECTION ITERATOR PICKLER FETCH   |               |       |       |            |          |
-------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   6 - access("X"."ID"=15)
37 rows selected.
As you can see the calculated cost is astronomical.
My understanding is that the optimizer in this case is guessing these numbers. If I look at the cost figures, it looks like an exponential increment:
8168 -> 8168^2 -> 8168^3 -> 8168^4
Where is the optimizer taking that 8168?
Is there any way to feed different values using dbms_stats.set_table_stats or some other method?
If I look at the structures created by Oracle, I see several object types defined as user defined types and varrays pairs. 
It seems that Oracle is assuming that the number of elements in the collections is 8168 at the first level, then 8168^2 at the second level and so on. 
Is my "theory" correct?
Thanks!
Flavio
 
 
November  04, 2005 - 2:44 am UTC 
 
 
 
Excellent!
Flavio Casetta, November  04, 2005 - 4:21 am UTC
 
 
Tom,
thank you very much for that link.
Best regards,
Flavio 
 
 
Difference in PIO inline view versus 'with' clause
Jack Douglas, December  19, 2005 - 1:12 pm UTC
 
 
Hi Tom,
The following appears to show a difference in IO behaviour between inline views and 'with' clauses - can you explain why the 'physical reads' is not zero for the second query?
SQL*Plus: Release 10.2.0.1.0 - Production on Mon Dec 19 17:56:38 2005
Copyright (c) 1982, 2005, Oracle.  All rights reserved.
Connected to:
Oracle Database 10g Release 10.2.0.1.0 - 64bit Production
TRACKER@oracle10> create table t as
  2  select rownum as id, 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' as dummy
  3  from dba_objects where rownum <= 10000;
Table created.
Elapsed: 00:00:00.14
TRACKER@oracle10> 
TRACKER@oracle10> analyze table t compute statistics;
Table analyzed.
Elapsed: 00:00:00.15
TRACKER@oracle10> 
TRACKER@oracle10> set autotrace on;
TRACKER@oracle10> 
TRACKER@oracle10> select count(*) from (select * from (select * from t where rownum > 0) union all select * from (se
  COUNT(*)
----------
     20000
Elapsed: 00:00:00.10
Execution Plan
----------------------------------------------------------
Plan hash value: 3104055737
------------------------------------------------------------------------
| Id  | Operation               | Name | Rows  | Cost (%CPU)| Time     |
------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |      |     1 |    10   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE         |      |     1 |            |          |
|   2 |   VIEW                  |      | 20000 |    10   (0)| 00:00:01 |
|   3 |    UNION-ALL            |      |       |            |          |
|   4 |     VIEW                |      | 10000 |     5   (0)| 00:00:01 |
|   5 |      COUNT              |      |       |            |          |
|*  6 |       FILTER            |      |       |            |          |
|   7 |        TABLE ACCESS FULL| T    | 10000 |     5   (0)| 00:00:01 |
|   8 |     VIEW                |      | 10000 |     5   (0)| 00:00:01 |
|   9 |      COUNT              |      |       |            |          |
|* 10 |       FILTER            |      |       |            |          |
|  11 |        TABLE ACCESS FULL| T    | 10000 |     5   (0)| 00:00:01 |
------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   6 - filter(ROWNUM>0)
  10 - filter(ROWNUM>0)
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
         88  consistent gets
          0  physical reads
          0  redo size
        335  bytes sent via SQL*Net to client
        338  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
TRACKER@oracle10> with tt as (select * from t where rownum > 0)
  2  select count(*) from (select * from tt union all select * from tt);
  COUNT(*)
----------
     20000
Elapsed: 00:00:15.24
Execution Plan
----------------------------------------------------------
Plan hash value: 3758785460
---------------------------------------------------------------------------------------------------------
| Id  | Operation                  | Name                       | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |                            |     1 |       |    15   (0)| 00:00:01 |
|   1 |  TEMP TABLE TRANSFORMATION |                            |       |       |            |          |
|   2 |   LOAD AS SELECT           |                            |       |       |            |          |
|   3 |    COUNT                   |                            |       |       |            |          |
|*  4 |     FILTER                 |                            |       |       |            |          |
|   5 |      TABLE ACCESS FULL     | T                          | 10000 |   517K|     5   (0)| 00:00:01 |
|   6 |   SORT AGGREGATE           |                            |     1 |       |            |          |
|   7 |    VIEW                    |                            | 20000 |       |    10   (0)| 00:00:01 |
|   8 |     UNION-ALL              |                            |       |       |            |          |
|   9 |      VIEW                  |                            | 10000 |       |     5   (0)| 00:00:01 |
|  10 |       TABLE ACCESS FULL    | SYS_TEMP_0FD9D6633_6D85FBA | 10000 |   517K|     5   (0)| 00:00:01 |
|  11 |      VIEW                  |                            | 10000 |       |     5   (0)| 00:00:01 |
|  12 |       TABLE ACCESS FULL    | SYS_TEMP_0FD9D6633_6D85FBA | 10000 |   517K|     5   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   4 - filter(ROWNUM>0)
Statistics
----------------------------------------------------------
        100  recursive calls
         51  db block gets
        145  consistent gets
         42  physical reads
       1388  redo size
        335  bytes sent via SQL*Net to client
        338  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed 
 
December  19, 2005 - 1:20 pm UTC 
 
it materialized the with subquery and that spilled into temp.  the physical IO was likely reads from temp.
one of the goals of the with subquery factoring is to materialize the result set once and use it repeatedly - which is what it did in this case. 
 
 
 
Thanks
Jack Douglas, December  19, 2005 - 1:49 pm UTC
 
 
Thanks Tom, that makes sense.
 
 
 
one more thing
Jack Douglas, December  19, 2005 - 2:27 pm UTC
 
 
Tom,
When you said "spilled over" into temp, did you mean to imply that if the materialized 'with' was small enough then this wouldn't happen?
It seems that materialized 'with' clauses will always generate at least some PIO no matter how small:
TRACKER@oracle10> set autotrace trace statistics;
TRACKER@oracle10> drop table t;
Table dropped.
TRACKER@oracle10> create table t as
  2  select rownum as id, 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' as dummy
  3  from dba_objects where rownum <= 100;
Table created.
TRACKER@oracle10> analyze table t compute statistics;
Table analyzed.
TRACKER@oracle10> with tt as (select * from t where rownum > 0)
  2  select count(*) from (select * from tt union all select * from tt);
Statistics
----------------------------------------------------------
        100  recursive calls
         10  db block gets
         22  consistent gets
          1  physical reads
       1432  redo size
        335  bytes sent via SQL*Net to client
        338  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
 
 
December  19, 2005 - 3:36 pm UTC 
 
tkprof it and see where the IO is happening (statistics_level = all before tracing)
Yes, if it can fit in ram, it might not need to do IO to and from temp. 
 
 
 
tkprof
Jack Douglas, December  20, 2005 - 5:19 am UTC
 
 
Here is the relevent output from tkprof. This confirms that we are doing both a physical write to, and a physical read from, the materialized table, doesn't it? 
My cache size is > 100Mb so that is not an issue in any of these examples.
I have noticed the same behaviour on 9ir2 as on 10gr2.
with tt as (select * from t where rownum > 0) select count(*) from (select * 
  from tt union all select * from tt)
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.09          0          0          1           0
Execute      1      0.00       0.18          0          3          7           0
Fetch        2      0.00       0.00          1          8          1           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.01       0.28          1         11          9           1
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 122  
Rows     Row Source Operation
-------  ---------------------------------------------------
      1  TEMP TABLE TRANSFORMATION  (cr=11 pr=1 pw=1 time=185361 us)
      1   LOAD AS SELECT  (cr=3 pr=0 pw=1 time=151741 us)
    100    COUNT  (cr=3 pr=0 pw=0 time=286 us)
    100     FILTER  (cr=3 pr=0 pw=0 time=178 us)
    100      TABLE ACCESS FULL T (cr=3 pr=0 pw=0 time=70 us)
      1   SORT AGGREGATE (cr=8 pr=1 pw=0 time=1286 us)
    200    VIEW  (cr=8 pr=1 pw=0 time=1050 us)
    200     UNION-ALL  (cr=8 pr=1 pw=0 time=848 us)
    100      VIEW  (cr=5 pr=1 pw=0 time=347 us)
    100       TABLE ACCESS FULL SYS_TEMP_0FD9D6658_6D85FBA (cr=5 pr=1 pw=0 time=243 us)
    100      VIEW  (cr=3 pr=0 pw=0 time=122 us)
    100       TABLE ACCESS FULL SYS_TEMP_0FD9D6658_6D85FBA (cr=3 pr=0 pw=0 time=19 us)
 
 
December  20, 2005 - 9:20 am UTC 
 
yes, it shows the one physical IO was to the temporary table that was created for this. 
 
 
 
is there another way?
Jack Douglas, December  20, 2005 - 10:02 am UTC
 
 
Hi Tom, 
Do you know of a way of avoiding this PIO? You normally say tune for LIO and PIO will take care of itself, but here we are stuck with PIO whatever we do.
In some cases we will have to use /*+ inline */ and lose the benefit of the temp table because reading several times from the cache is faster than once from the disk.
This seems like it might be a serious limitation of 'with' clauses - do you think it would be worth making an enhancement request?
Jack 
 
December  20, 2005 - 10:29 am UTC 
 
don't materialize it?  use inline views. 
 
 
 
Encancement Request
Jack Douglas, December  28, 2005 - 5:21 am UTC
 
 
Hi Tom,
For the sake of anyone else who is interested, here is the bug number for the enhancement request I made: 4912547
Happy Christmas to you and thanks for giving us your time - I learn a lot from this site.
Jack 
 
 
WITH clause is not working for nested table
Rajeswari, April     19, 2006 - 7:38 am UTC
 
 
scott@ORA9.2> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.6.0 - 64bit Production
PL/SQL Release 9.2.0.6.0 - Production
CORE    9.2.0.6.0       Production
TNS for HPUX: Version 9.2.0.6.0 - Production
NLSRTL Version 9.2.0.6.0 - Production
 CREATE or replace TYPE NUMERIC_ID_TABLE_TYPE AS TABLE OF NUMBER(9)
/
declare
   cursor c1(arg1 numeric_id_table_type) is
    with test_view as (select column_value from table(cast(arg1 as numeric_id_table_type)) )
                   select column_value from test_view 
     union 
            select column_value from test_view;
  
   var1 number(9);
  
  begin
           open c1(numeric_id_table_type(1, 2, 3, 4, 5));
  
           fetch c1 into var1;
  
           while c1%found loop
          dbms_output.put_line('var1 = ' || var1);
        fetch c1 into var1;
           end loop;
  
           close c1;
  
  end;
  /
declare
*
ERROR at line 1:
ORA-00932: inconsistent datatypes: expected - got -
ORA-06512: at line 3
ORA-06512: at line 11
If I remove union or with clause it is working fine. Whether it is limitation of with clause?
 
 
April     19, 2006 - 9:04 am UTC 
 
worked in 10gr2, but not 9ir2 - please contact support to work through this one. 
 
 
 
Elapsed time
Raju, May       18, 2006 - 2:41 pm UTC
 
 
Hi Tom,
I rewrote a poorly performing query using the "WITH" clause and saw that the consistent gets and the physical reads are reduced by 1/3rd. But now I find that the elapsed time has increased by about 20% i,e ran in SQL*Plus with timing switched ON. Now, I really don't know if I should revert to the old query or use the new query as it has better consistent/physical read values. The query will be used in the overnight batch process. Please can you advice?
Thanks
 
 
May       19, 2006 - 10:16 am UTC 
 
use tkprof, set timing on in sqlplus isn't particularly enlightening.  tkprof will show the work (cpu/ela/ios) and compare those two. 
 
 
 
WITH clause significantly slower than inline view when using CONNECT BY
Mohit Agarwal, May       25, 2006 - 3:17 pm UTC
 
 
I have a query that needs to return a set of loans related by a parent-child relationship given any loan in that family of loans.
SELECT loanid
  FROM (
SELECT loanid
  FROM loan
CONNECT BY loanid = PRIOR parentloanid
 START WITH loanid = <some loanid>
 ORDER BY level DESC)
 WHERE rownum = 1;
This gives me the original parent loan. Cost of this query is 3. LoanID is PK and an index exists on Parentloanid.
I then use
SELECT loanid
  FROM loan
CONNECT BY parentloanid = PRIOR loanid
 START WITH loanid = <the loanid from previous query>.
Cost is 4.
If I combine them
WITH l AS (SELECT loanid
  FROM (
SELECT loanid
  FROM loan
CONNECT BY loanid = PRIOR parentloanid
 START WITH loanid = <some loanid>
 ORDER BY level DESC)
 WHERE rownum = 1)
SELECT loanid
  FROM loan
CONNECT BY parentloanid = PRIOR loanid
 START WITH loanid = (SELECT loanid FROM L)
The cost blows up to 13126. It starts doing FTS in both queries when using the CONNECT BY (CONNECT BY PUMP section) where earlier it used an INDEX SCAN. 
If I used an inline view the cost is 8
SELECT loanid
  FROM loan
CONNECT BY parentloanid = PRIOR loanid
 START WITH loanid = 
(SELECT loanid
  FROM (
SELECT loanid
  FROM loan
CONNECT BY loanid = PRIOR parentloanid
 START WITH loanid = <some loanid>
 ORDER BY level DESC)
 WHERE rownum = 1)
Is there perhaps an issue of using a WITH clause and a CONNECT BY clause in the same query? 
 
 
WITH clause significantly slower than inline view when using CONNECT BY
Mohit Agarwal, May       25, 2006 - 3:42 pm UTC
 
 
I made a mistake in the cost I posted. The cost for the WITH clause query is 9173 and its doing the FTS only for the query in the WITH clause.
By itself the plan for the query in the WITH clause is
SELECT STATEMENT   Cost = 3 
VIEW
  COUNT STOPKEY   
    VIEW
      SORT ORDER BY STOPKEY   
        CONNECT BY WITH FILTERING   
          TABLE ACCESS BY INDEX ROWID PRODORA.LOAN  
            INDEX UNIQUE SCAN PRODORA.PK_LOAN 
          NESTED LOOPS
            BUFFER SORT 
              CONNECT BY PUMP 
            TABLE ACCESS BY INDEX ROWID PRODORA.LOAN  
              INDEX UNIQUE SCAN PRODORA.PK_LOAN 
          TABLE ACCESS FULL PRODORA.LOAN 
Using the WITH clause it changed to
SELECT STATEMENT   Cost = 9173  
  TEMP TABLE TRANSFORMATION   
    LOAD AS SELECT  
      VIEW
        COUNT STOPKEY   
          VIEW
            SORT ORDER BY STOPKEY   
              CONNECT BY WITH FILTERING   
                TABLE ACCESS BY INDEX ROWID PRODORA.LOAN  
                  INDEX UNIQUE SCAN PRODORA.PK_LOAN 
                HASH JOIN   
                  CONNECT BY PUMP 
                  TABLE ACCESS FULL PRODORA.LOAN
                TABLE ACCESS FULL PRODORA.LOAN 
    CONNECT BY WITH FILTERING   
      TABLE ACCESS BY INDEX ROWID PRODORA.LOAN  
        INDEX UNIQUE SCAN PRODORA.PK_LOAN 
          VIEW
            TABLE ACCESS FULL SYS.SYS_TEMP_0FD9D68B2_DD869F54   
      NESTED LOOPS
        BUFFER SORT 
          CONNECT BY PUMP 
        TABLE ACCESS BY INDEX ROWID PRODORA.LOAN  
          INDEX RANGE SCAN PRODORA.LOAN_NDX10   
      TABLE ACCESS FULL PRODORA.LOAN
      VIEW
        TABLE ACCESS FULL SYS.SYS_TEMP_0FD9D68B2_DD869F54
As you can see in the top half of the plan, it stopped using the PK in the CONNECT BY looping. The Loan table has about 3M records in it.  
Thanks 
 
 
With Clause and ora-3001
A reader, December  18, 2007 - 5:10 am UTC
 
 
Hi Tom, 
   I've got a view created under a user that uses a With clause.  When I try to read from the view as the owner it works fine. However, when I try a select from the view as another user, I'm hitting Ora-03001:- Unimplemented feature.  Have you seen this before?  I've tried the With clause with/without the Materialize hint. But it just doesn't seem to like the With clause at all. Oh, we are on 9ir2. 
Thanks 
December  18, 2007 - 1:54 pm UTC 
 
need help reproducing
ops$tkyte%ORA9IR2> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.8.0 - Production
PL/SQL Release 9.2.0.8.0 - Production
CORE    9.2.0.8.0       Production
TNS for Linux: Version 9.2.0.8.0 - Production
NLSRTL Version 9.2.0.8.0 - Production
ops$tkyte%ORA9IR2> create user a identified by a;
User created.
ops$tkyte%ORA9IR2> grant create session to a;
Grant succeeded.
ops$tkyte%ORA9IR2>
ops$tkyte%ORA9IR2> create or replace view v
  2  as
  3  with data
  4  as
  5  (select sysdate+level dt from dual connect by level <= 5)
  6  select * from data
  7  /
View created.
ops$tkyte%ORA9IR2>
ops$tkyte%ORA9IR2> select * from v;
DT
---------
19-DEC-07
20-DEC-07
21-DEC-07
22-DEC-07
23-DEC-07
ops$tkyte%ORA9IR2> grant select on v to a;
Grant succeeded.
ops$tkyte%ORA9IR2> @connect a/a
ops$tkyte%ORA9IR2> set termout off
a%ORA9IR2>
a%ORA9IR2> set termout on
a%ORA9IR2> select * from ops$tkyte.v;
DT
---------
19-DEC-07
20-DEC-07
21-DEC-07
22-DEC-07
23-DEC-07
 
 
 
With clause and ora-3001 - Update
A reader, December  18, 2007 - 5:46 am UTC
 
 
Hi Tom, 
  I managed to sort this one out thanks.  I had a packaged function call in my subquery, which it didn't like.  I think it is a restriction of some sort in 9ir2.  Apparently you can't use the With Clause and a function call together.   
Regards,
Chandini 
 
December  18, 2007 - 1:56 pm UTC 
 
still need help...
ops$tkyte%ORA9IR2> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.8.0 - Production
PL/SQL Release 9.2.0.8.0 - Production
CORE    9.2.0.8.0       Production
TNS for Linux: Version 9.2.0.8.0 - Production
NLSRTL Version 9.2.0.8.0 - Production
ops$tkyte%ORA9IR2>
ops$tkyte%ORA9IR2> create or replace package foo
  2  as
  3     function bar return number;
  4  end;
  5  /
Package created.
ops$tkyte%ORA9IR2> create or replace package body foo
  2  as
  3     function bar return number as begin return 1; end;
  4  end;
  5  /
Package body created.
ops$tkyte%ORA9IR2>
ops$tkyte%ORA9IR2> create user a identified by a;
User created.
ops$tkyte%ORA9IR2> grant create session to a;
Grant succeeded.
ops$tkyte%ORA9IR2>
ops$tkyte%ORA9IR2> create or replace view v
  2  as
  3  with data
  4  as
  5  (select sysdate+level+foo.bar dt from dual connect by level <= 5)
  6  select foo.bar, dt from data
  7  /
View created.
ops$tkyte%ORA9IR2>
ops$tkyte%ORA9IR2> select * from v;
       BAR DT
---------- ---------
         1 20-DEC-07
         1 21-DEC-07
         1 22-DEC-07
         1 23-DEC-07
         1 24-DEC-07
ops$tkyte%ORA9IR2> grant select on v to a;
Grant succeeded.
ops$tkyte%ORA9IR2> @connect a/a
ops$tkyte%ORA9IR2> set termout off
a%ORA9IR2>
a%ORA9IR2> set termout on
a%ORA9IR2> select * from ops$tkyte.v;
       BAR DT
---------- ---------
         1 20-DEC-07
         1 21-DEC-07
         1 22-DEC-07
         1 23-DEC-07
         1 24-DEC-07
 
 
 
Yes but....
A reader, December  19, 2007 - 4:46 am UTC
 
 
Hi tom, 
  Thanks for the example.  
  If you change your view definition to access the 'data' subquery more than once, you hit the problem.  
Example:-  
As user A, I created the following objects 
SQL> create or replace package foo 
  2  as 
  3    function bar return number;
  4  end;
  5  /
Package created.
SQL> create or replace package body foo 
  2  as 
  3    function bar return number 
  4    as 
  5    begin 
  6       return 1;
  7    end bar;
  8  end;
  9  /
Package body created.
SQL> create or replace view test 
  2  as 
  3  with tt 
  4  as (select sysdate, foo.bar 
  5      from dual 
  6      where 1 = foo.bar) 
  7  select * from tt;
View created.
Now as User B, when I run 
SQL> select * from cpaterson.test;
SYSDATE          BAR
--------- ----------
19-DEC-07          1
It works.  
However, change the definition slightly as User A to :- 
SQL> create or replace view test 
  2  as 
  3  with tt 
  4  as (select sysdate, foo.bar 
  5      from dual 
  6      where 1 = foo.bar) 
  7  select * from tt
  8  union all 
  9  select * from tt;  --I'm reading from tt more than once.
View created.
And now, when I try to read from this view as User B, I hit the error - 
SQL> l
  1* select * from cpaterson.test
SQL> /
select * from cpaterson.test
                           *
ERROR at line 1:
ORA-03001: unimplemented feature
I think it doesn't like it when you call the subquery in the With clause more than once and you've used function calls in the SQL too.  
To test this, I changed the view defn again to 
 create or replace view test 
as 
with tt 
as (select sysdate, 1 
     from dual 
     where 1 = 1) 
select * from tt
union all 
select * from tt;
And now from User B, it works pretty fine.
SQL> l
  1* select * from cpaterson.test
SQL> /
SYSDATE            1
--------- ----------
19-DEC-07          1
19-DEC-07          1
If you can explain why this is happening, that would be great.  
Thanks, 
Chandini 
 
 
December  19, 2007 - 10:40 am UTC 
 
Ok, it is a 9i issue - fixed in 10g
In 9i, a workaround can be alter session set current_schema - it is a name resolution issue of some sort apparently.  see example below.
In any event, will not reproduce in 10g, and if you wish, you may contact support and reference bug #4483232 in 9i.
ops$tkyte%ORA9IR2> create or replace view v1
  2  as
  3  with tt
  4  as (select sysdate, foo.bar
  5      from dual
  6      where 1 = foo.bar)
  7  select * from tt;
View created.
ops$tkyte%ORA9IR2>
ops$tkyte%ORA9IR2> create or replace view v2
  2  as
  3  with tt
  4  as (select sysdate, foo.bar
  5      from dual
  6      where 1 = foo.bar)
  7  select * from tt
  8  union all
  9  select * from tt;
View created.
ops$tkyte%ORA9IR2> grant select on v1 to a;
Grant succeeded.
ops$tkyte%ORA9IR2> grant select on v2 to a;
Grant succeeded.
ops$tkyte%ORA9IR2> @connect a/a
ops$tkyte%ORA9IR2> set termout off
a%ORA9IR2>
a%ORA9IR2> set termout on
a%ORA9IR2> select * from ops$tkyte.v1;
SYSDATE          BAR
--------- ----------
19-DEC-07          1
a%ORA9IR2> select * from ops$tkyte.v2;
select * from ops$tkyte.v2
                         *
ERROR at line 1:
ORA-03001: unimplemented feature
a%ORA9IR2> alter session set current_schema=ops$tkyte;
Session altered.
a%ORA9IR2> select * from v1;
SYSDATE          BAR
--------- ----------
19-DEC-07          1
a%ORA9IR2> select * from v2;
SYSDATE          BAR
--------- ----------
19-DEC-07          1
19-DEC-07          1
 
 
 
Thanks
A reader, December  19, 2007 - 11:12 am UTC
 
 
Thanks for your quick reply and explanation. It is always helpful when you know why some things don't work.  
I initially thought it was maybe a problem with the With clause creating a MV under the covers and user B missing certain privileges.  
Thanks
Chandini  
 
Create table with IN list 
bhasker, March     06, 2008 - 4:01 pm UTC
 
 
I have a query with CTAS, which has poor perf. I tried to rewrite using WITH, but it seems cost has gone UP. How can we rewrite below query?
CREATE TABLE tmp_MP_display_xflag_M
--PARALLEL 4 TABLESPACE FT_WORKAREA NOLOGGING 
AS SELECT /*+ ORDERED USE_HASH(x,d) INDEX_FFS(x) PARALLEL(d,4) */
    d.ROWID dis_rowid
  FROM MP_global_exclude PARTITION (M20080201) x,
    display PARTITION (M20080201)  d
 WHERE x.dsp_hash IN
                (d.dsp_hash,
                 TRUNC (d.dsp_hash, -14),
                 TRUNC (d.dsp_hash, -7),
                 TRUNC (d.dsp_hash, -14) + MOD (d.dsp_hash, POWER (10, 7))
                )
   AND LOWER (d.url_name) LIKE x.rule || '%'
   AND BITAND (d.xflag, 1) = 0
   AND BITAND (d.xflag, 1) = 0
Thanks,
Bhasker 
March     07, 2008 - 5:35 pm UTC 
 
first, lose the hints, stop hinting.  let the database do what databases do.
and then second, wow, that is the most bizarre join I've seen.  You really want that bitand to happen...  lower(d.c) like x.c || '%'... x.c in (d.c, f(d.c), g(d.c), h(d.c) )...
ouch - so, basically, like a big old cartesian join (your ordered hint funny enough almost looks like it would be backwards... 
with data
as
(select rowid rid,
        decode(r, 1,dsp_hash,
                  2, TRUNC (d.dsp_hash, -14),
                  3, TRUNC (d.dsp_hash, -7),
                  4, TRUNC (d.dsp_hash, -14) + MOD (d.dsp_hash, POWER (10, 7)) ) dsp_hash,
        lower(url_name)
   from display PARTITION (M20080201)  d,
       (select level r from dual connect by level <= 4)
  where bitand(xflag,1) = 0
)
select distinct data.rid
  from data, MP_global_exclude PARTITION (M20080201) x
 where data.url_name like x.rule||'%'
   and data.dsp_hash = x.dsp_hash
/now we can actually hash join on something.  And if you have an index on X(rule,dsp_hash) it'll fast full scan it all by itself. 
 
 
great... great...
Bhasker, March     10, 2008 - 9:14 pm UTC
 
 
Thanks a lot Tom!..
This is my first posting.. Always, i use to READ the questions and get answers for my queries. 
The query is working fine and improved performance from 1 hr 23 mins to 1 hr 10 mins.. but, we're going to run same query for 10 times or so.. so we will save 100 mins..  
Just wondering, if there's anything we can do to get better performance?. May be some other technique, rather than WITH Clause??? 
Because, i got DISPLAY table for 25 milliion records and EXCLUDE table for 250K records. Thats the reason, we gave ORDERED HINT to use small table first for JOIN.
Thanks a lot again.. 
Bhasker 
March     10, 2008 - 9:26 pm UTC 
 
oracle does not need the ordered hint, we actually KNOW what table is smaller if statistics are present.  Your join was NO JOIN - it was horrible - please look at your original stuff and just try to understand what sort of processing would have to take place (eg: UNDERSTANDING is key to tuning, look at what you were asking the computer to do and ask yourself "could you do this efficiently", if you as thinking human being cannot - take pity on the computer.  I'll update this in 10 years when the computers start to win, hands down)
one would ask the obvious question:
why do you need to run it 10 times or so
why cannot you run it ONCE 
 
 
WITH Clause ... thanks.. 
Bhasker, March     10, 2008 - 11:40 pm UTC
 
 
Actually, we have 10 reports which work on 10 different schemas. So, same query will be running for 10 schemas.That's why i said, i need to run 10 times.
Basically, performance issue was due to table DISPLAY getting FULL TABLE SCANE four times due to IN clause. 
To reduce this 4 times full table scan, i thought it was better to use WITH Clause ?. Also, i guess it would help to create an index on dsp_hash. But, ALL records in DISPLAY table partition have to be processed, so developers did not create index on dsp_hash...
WHERE x.dsp_hash IN
                (d.dsp_hash,
                 TRUNC (d.dsp_hash, -14),
                 TRUNC (d.dsp_hash, -7),
                 TRUNC (d.dsp_hash, -14) + MOD (d.dsp_hash, POWER (10, 7))
                )
but the WITH clause query you gave has taught me few more things in Oracle.. Thanks a lot! As everyone writes.. you're really great!!!
Bhasker 
 
WITH Clause
Maverick, March     28, 2008 - 3:16 pm UTC
 
 
Tom,
  From EMP Table, I was looking at the following info. basically some counts 
   count(empno less than 25),
   count(empno between 25 and 50),
   count(deptno for empno between 30 and 40)
 in one query.
 The tables I'm trying to do this has a couple of million records and evergrowing, so I was wondering using WITH can I accomplish this.
     
  using WITH clause can we write a query something like this?
  With data as(select * from EMP)
       select count(empno) from data 
        where empno between 1 and 25,
       select count(empno) from data
        where empno between 26 and 99,
       select count(empno) from data
        where empno between 100 and 199;
  ????
Thanks, 
March     31, 2008 - 7:48 am UTC 
 
select count( case when empno < 25 then 1 end ) cnt1,
       count( case when empno between 25 and 50 then 1 end ) cnt2,
       count( case when empno between 26 and 99 then 1 end ) .......
Just do it in a SINGLE pass, don't read and re-read the table over and over.
And that WITH clause above - if it decided to materialize the data, you would only accomplish adding an ADDITIONAL FULL SCAN!!! and a WRITE TO TEMP of the data. 
 
 
 
Maverick, March     31, 2008 - 8:13 am UTC
 
 
Tom, thanks for your response. couple of quick questions [just to make sure before I implement this stuff]
Doesn't "Count(*)" result in a full table scan? I want to avoid full table scans as it has millions of rows.
so, can I do 
select count(case when empno<25 then 1 end ) cnt1,
       count(case when empno between 26 and 50 then 1 end ) cnt2
from (select empno from emp when empno <=500)
??
[I was thinking that's what you meant, above]
Thanks for your inputs. 
March     31, 2008 - 9:44 am UTC 
 
YOU WANT A FULL SCAN - either of an index on empno (presuming empno is not null or is in an index with at least one not null attribute) OR of the TABLE.
count(*) can use an index fast full scan.
Yes, you can:
select count(case when empno<25 then 1 end ) cnt1,
       count(case when empno between 26 and 50 then 1 end ) cnt2
from emp
where empno <500;
of course - hopefully you understand what this SQL statement is doing....
All I can ever do is take your inputs to me, which were simply:
...
 With data as(select * from EMP)
       select count(empno) from data 
        where empno between 1 and 25,
       select count(empno) from data
        where empno between 26 and 99,
       select count(empno) from data
        where empno between 100 and 199;
.....
(nothing about a predicate, nothing about just a small bit of data, NOTHING)
and answer based on that, if you can add a predicate - go for it. 
But to get these counts you definitely do NOT NEED more than a single scan. 
 
 
Follow up
Maverick, April     22, 2008 - 9:22 am UTC
 
 
Tom, I am just following up for the above question: 
   I am going to implement a single pass SQL and not use WITH as you mentioned [makes sense]. But is it possible to write a query like that at all? I mean syntactically is it correct?
I keep getting
ORA-00933: SQL command not properly ended
Error. 
So, is it not supported?
Thanks, 
April     23, 2008 - 5:53 pm UTC 
 
so, no example??!?!?!?!?!?
query I gave you works just dandy...
ps$tkyte%ORA10GR2> connect scott/tiger
Connected.
select count(case when empno<25 then 1 end ) cnt1,
    count(case when empno between 26 and 50 then 1 end ) cnt2
from emp
  4  where empno <500;
      CNT1       CNT2
---------- ----------
         0          0
 
 
 
Example????
Maverick, April     24, 2008 - 9:22 am UTC
 
 
Tom, May be you missed the part I wrote "Following above questions by me" in which I had that example of WITH inline clause:
anyway I am giving it again:
With data as(select * from EMP) 
    select count(empno) from data 
    where empno between 1 and 25, 
    select count(empno) from data 
    where empno between 26 and 99, 
    select count(empno) from data 
    where empno between 100 and 199; 
And I know your query works just dandy [and that's what I need and am using it] 
My questions is, can we write a query like this [Syntactically] is it possible? 
If possible, why I keep getting above mentioned oracle message?
Thanks, 
April     28, 2008 - 12:05 pm UTC 
 
i missed nothing.
you wrote:
...
I keep getting
ORA-00933: SQL command not properly ended
Error. 
So, is it not supported?
Thanks,
....
I have no idea what query that error applied to.  I had to assume you meant MY query that YOU modified, or your query - but in any case, I have no idea what query that error applies to because there is....
NO EXAMPLE.
I guess now, reading this followup, you meant YOUR query (your query that I ignored since the answer was "do not do that")
ops$tkyte%ORA9IR2> with data as (select * from scott.emp)
  2  select '7000/7500', count(*) from data where empno between 7000 and 7500
  3  union all
  4  select '7500/8000', count(*) from data where empno between 7501 and 8000
  5  /
'7000/750   COUNT(*)
--------- ----------
7000/7500          2
7500/8000         12
 
 
 
RE: example
Duke Ganote, April     24, 2008 - 9:32 am UTC
 
 
Maverick-- Are you looking for values in rows rather than columns?  Something more like this?
WITH sample_data as ( select * from scott.emp )
select count(*), 1 as mn, 25 as mx
from sample_data where empno between 1 and 25 union all
select count(*), 26, 99
from sample_data where empno between 26 and 99 union all
select count(*), 99, null
from sample_data where empno > 99
/
       COUNT(*)         MN         MX
--------------- ---------- ----------
              0          1         25
              0         26         99
             14         99 
 
What makes Oracle decide one way or the other?
oj, September 30, 2008 - 7:15 pm UTC
 
 
Hi Tom,
I have a query that I wrote for a complex report. It is a mix of hierarchial query with some analytic functions and lots of aggregates....oh and a whole lot of case stuctures..
Once the data is available,we compare two identical result sets and do some more of the things above...
When I try to use "subquery factoring", oracle seems to be doing it per the plan, but returns the results slower...
My query goes into 300 lines so am unable to post here.  But will post the before and after plan in a separate post..
would this be sufficient information? 
October   01, 2008 - 11:51 am UTC 
 
sufficient for what?
you have two queries.
They are logically equivalent (return same data).
one is faster than the other.
this happens all of the time - nothing is every "always better" than something else.  scalar subqueries work best for
o first rows optimization type queries - need the first rows as soon as possible
o small result sets
they are not suited for really large queries as they tend to force the optimizer into a "do it yourself nested loop"
Consider;
select t.*, (select count(*) from t2 where t2.something = t1.something) cnt
  from t;
Conceptually that is evaluated similar to this:
for x in (select * from t)
loop
   select count(*) into :cnt from t2 where t2.something = :X.SOMETHING;
   output row
end loop
Now, if T is large and t2.something is indexed and t2.something is fairly selective (returns a small number of rows, cnt is a small number) then a scalar subquery MIGHT be useful to get the first rows really fast - just full scan a bit of T, run the scalar subquery a couple of times and there you go.
However, if T is large and you are selecting ALL ROWS - the goal is to get the last row as fast as possible - I'd almost certainly want to not consider a scalar subquery. I'd rather:
select *
  from T, (select count(*) from t2 group by something) x,
 where t.something = x.something(+)  -- (+) only if NECESSARY of course
nice big full scans, nice efficient bulk hash join - that'd be what I wanted to see there. 
 
 
What makes Oracle decide one way or the other? 
oj, September 30, 2008 - 7:17 pm UTC
 
 
WITH temp_table_aggr AS
(
SELECT /* materialize */
   team_buckets.agent_name,
   team_buckets.bckt_name,
   team_buckets.cbp,
   team_buckets.lvl,
   SUM(NVL(activity.loan_nums,0)) ln_cts_tot,
   SUM(NVL(activity.negs,0)) negs,
   SUM(NVL(activity.pos,0)) pos,
   SUM(NVL(activity.nos,0)) nos,
   SUM(NVL(activity.bom,0)) bom,
   SUM(NVL(activity.eom,0)) eom,
   SUM(NVL(activity.neg_bal,0)) neg_bal,
   SUM(NVL(activity.pos_bal,0)) pos_bal,
   SUM(NVL(activity.nos_bal,0)) nos_bal
FROM
   (
     SELECT
        COUNT(VW_DFLT_LOAN_BCKT_FCT.DFLT_LOAN_KEY) loan_nums,
        TEAM_KEY,
        ACTVTY_TIME_KEY,
        BCKT_KEY,
             CASE
                WHEN loan_actvty < 0 THEN
                   COUNT(VW_DFLT_LOAN_BCKT_FCT.DFLT_LOAN_KEY)
                ELSE
                   0
             END negs,
             CASE
                WHEN loan_actvty > 0 THEN
                   COUNT(VW_DFLT_LOAN_BCKT_FCT.DFLT_LOAN_KEY)
                ELSE
                   0
             END pos,
             CASE
                WHEN loan_actvty = 0 THEN
                   COUNT(VW_DFLT_LOAN_BCKT_FCT.DFLT_LOAN_KEY)
                ELSE
                   0
             END nos,
             CASE
                WHEN VW_DFLT_LOAN_BCKT_FCT.ACTVTY_TIME_KEY = DFLTDIMMGR.BUSN_DAY(20080901,'P') THEN
                   COUNT(VW_DFLT_LOAN_BCKT_FCT.DFLT_LOAN_KEY)
                ELSE
                   0
             END bom,
             CASE
                WHEN VW_DFLT_LOAN_BCKT_FCT.ACTVTY_TIME_KEY = DFLTDIMMGR.BUSN_DAY(20080924,'P') THEN
                   COUNT(VW_DFLT_LOAN_BCKT_FCT.DFLT_LOAN_KEY)
                ELSE
                   0
             END eom,
             CASE
                WHEN loan_actvty < 0 THEN
                   SUM(DFLT_LOAN_FCT.UNPAID_PRINCIPAL_BAL)
                ELSE
                   0
             END neg_bal,
             CASE
                WHEN loan_actvty > 0 THEN
                   SUM(DFLT_LOAN_FCT.UNPAID_PRINCIPAL_BAL)
                ELSE
                   0
             END pos_bal,
             CASE
                WHEN loan_actvty = 0 THEN
                   SUM(DFLT_LOAN_FCT.UNPAID_PRINCIPAL_BAL)
                ELSE
                   0
             END nos_bal
      FROM  DFLTMRTMGR.VW_DFLT_LOAN_BCKT_FCT,
            DFLTDIMMGR.DFLT_LOAN_DIM,
            DFLTMRTMGR.DFLT_LOAN_FCT
      WHERE VW_DFLT_LOAN_BCKT_FCT.ACTVTY_TIME_KEY BETWEEN 20080901 AND 20080924
      AND VW_DFLT_LOAN_BCKT_FCT.DFLT_LOAN_KEY = DFLT_LOAN_DIM.DFLT_LOAN_KEY
   AND VW_DFLT_LOAN_BCKT_FCT.TIME_KEY = DFLT_LOAN_FCT.TIME_KEY
      AND DFLT_LOAN_DIM.DFLT_LOAN_KEY = DFLT_LOAN_FCT.DFLT_LOAN_KEY
      --(select param_val from STGEDWMGR.ctl_global_parm where param_name = 'DFLT_BATCH_DATE')
      GROUP BY ACTVTY_TIME_KEY,VW_DFLT_LOAN_BCKT_FCT.TIME_KEY,TEAM_KEY,VW_DFLT_LOAN_BCKT_FCT.BCKT_KEY,loan_actvty
      ) activity,
   (
   SELECT CD_KEY,bckt_name,
       TEAM_KEY,AGENT_NAME,
       cbp,lvl
   FROM
      (
        SELECT CD_KEY, cd_desc bckt_name
        FROM DFLTDIMMGR.CODE_DIM
        WHERE CD_CTGRY = 'BUCKET'
        --AND SYSDATE BETWEEN from_eff_dt AND to_eff_dt
        ) buckets,
        (
   SELECT
      TEAM_KEY,
      LPAD('>',6*(LEVEL-1),'--') ||PARTICIPANT_LAST_NM||','
      ||PARTICIPANT_FRST_NM AGENT_NAME,
      sys_connect_by_path(substr(PARTICIPANT_FRST_NM,0,1)||PARTICIPANT_LAST_NM, '/') cbp,
      level lvl
   FROM DFLTDIMMGR.DFLT_TEAM_DIM
   START WITH PARTICIPANT_ID = '1227424'
   CONNECT BY PRIOR PARTICIPANT_ID = PARENT_PARTICIPANT_ID
   ) team
  WHERE 1 = 1
  ) team_buckets
WHERE team_buckets.cd_key = activity.BCKT_KEY(+)
AND team_buckets.team_key = activity.team_key(+)
GROUP BY team_buckets.agent_name,
   team_buckets.lvl,
   team_buckets.cbp,
   team_buckets.bckt_name
)
SELECT AGENT_NAME,bckt_name,cbp,lvl,
     (max(lvl) over (partition by 1)-(lvl-1)) reverse_level,
     NVL(SUM(bom),0),NVL(SUM(eom),0),
     NVL(SUM(tot),0),NVL(SUM(tot_negs),0),NVL(SUM(tot_pos),0),NVL(SUM(tot_nos),0),
     NVL(SUM(tot_neg_bal),0),NVL(SUM(tot_pos_bal),0),NVL(SUM(tot_nos_bal),0)
FROM
(
SELECT
   a.agent_name,
   a.lvl,
   a.cbp,
   NVL(a.bckt_name,b.bckt_name) bckt_name,
   case
      SUBSTR(b.cbp,1,LENGTH(a.cbp))
         when a.cbp then
            CASE
               WHEN NVL(a.bckt_name,b.bckt_name) = b.bckt_name THEN
                   SUM(b.eom)
               ELSE
                  0
            END
         else
            NULL
   end eom,
   case
      SUBSTR(b.cbp,1,LENGTH(a.cbp))
         when a.cbp then
            CASE
               WHEN NVL(a.bckt_name,b.bckt_name) = b.bckt_name THEN
                   SUM(b.bom)
               ELSE
                  0
            END
         else
            NULL
   end bom,
   case
      SUBSTR(b.cbp,1,LENGTH(a.cbp))
         when a.cbp then
            CASE
               WHEN NVL(a.bckt_name,b.bckt_name) = b.bckt_name THEN
                   SUM(b.ln_cts_tot)
               ELSE
                  0
            END
         else
            NULL
   end tot,
   case
      SUBSTR(b.cbp,1,LENGTH(a.cbp))
         when a.cbp then
            CASE
               WHEN NVL(a.bckt_name,b.bckt_name) = b.bckt_name THEN
                   SUM(b.negs)
               ELSE
                  0
            END
         else
            NULL
   end tot_negs,
   case
      SUBSTR(b.cbp,1,LENGTH(a.cbp))
         when a.cbp then
            CASE
               WHEN NVL(a.bckt_name,b.bckt_name) = b.bckt_name THEN
                   SUM(b.pos)
               ELSE
                  0
            END
         else
            NULL
   end tot_pos,
   case
      SUBSTR(b.cbp,1,LENGTH(a.cbp))
         when a.cbp then
            CASE
               WHEN NVL(a.bckt_name,b.bckt_name) = b.bckt_name THEN
                   SUM(b.nos)
               ELSE
                  0
            END
         else
            NULL
   end tot_nos,
   case
      SUBSTR(b.cbp,1,LENGTH(a.cbp))
         when a.cbp then
            CASE
               WHEN NVL(a.bckt_name,b.bckt_name) = b.bckt_name THEN
                   SUM(b.neg_bal)
               ELSE
                  0
            END
         else
            NULL
   end tot_neg_bal,
   case
      SUBSTR(b.cbp,1,LENGTH(a.cbp))
         when a.cbp then
            CASE
               WHEN NVL(a.bckt_name,b.bckt_name) = b.bckt_name THEN
                   SUM(b.pos_bal)
               ELSE
                  0
            END
         else
            NULL
   end tot_pos_bal,
   case
      SUBSTR(b.cbp,1,LENGTH(a.cbp))
         when a.cbp then
            CASE
               WHEN NVL(a.bckt_name,b.bckt_name) = b.bckt_name THEN
                   SUM(b.nos_bal)
               ELSE
                  0
            END
         else
            NULL
   end tot_nos_bal
FROM
temp_table_aggr a,
temp_table_aggr b
GROUP BY a.agent_name,a.bckt_name,b.bckt_name,a.cbp,b.cbp,a.lvl
)
WHERE bckt_name IS NOT NULL AND tot <> 0
GROUP BY AGENT_NAME,bckt_name,cbp,lvl
ORDER BY cbp
 
October   01, 2008 - 11:52 am UTC 
 
eh?  
first, what use is posting a 15 page query - no one could digest that.
Second - how could anyone answer:
"What makes Oracle decide one way or the other?"
one way or the other about what exactly??? 
 
 
What makes Oracle decide one way or the other?
oj, October   01, 2008 - 12:36 pm UTC
 
 
Sorry about that. I put the query in desperation. What is happening is that the query works perfectly fine if I use  two views (traditional way). As soon as I put it in this way with subquery factoring, it shows a drastically low cost but does not return the result for a long long time and then gives no rows returned.
I was sure that I must have done some copy paste mistake but re-checked it and did not find any. 
Looking at a query like this, would there be a nicer way to write it? I was thinking of trying the ' Model dimension measure' construct. 
October   01, 2008 - 1:17 pm UTC 
 
why do you have hints in there.  You are forcing the optimizer to do something it might not want to do.
Are you saying "this query should return rows, provably so" but does not? If so, please contact support with the test case.
 
 
 
What makes Oracle decide one way or the other?
oj, October   01, 2008 - 2:36 pm UTC
 
 
Thanks for all your responses.
The hint was just one of the trial and error things I did. Did not make a difference to the plan or results.
I will contact support and see if they can validate/verify.
Thanks again. 
 
How to declare a cursor in Pro*C using the WITH clause?
rama, October   15, 2008 - 7:50 pm UTC
 
 
Hi Tom,
I am modifying an existing Pro*C process that reads in a lot of data. Right now it is declared in the form:
   EXEC SQL DECLARE <cursor_name> CURSOR FOR 
        SELECT columns
        FROM  table1, table2....
        WHERE <where_clause>;
How would I modify the cursor to use the WITH clause?
Thanking you
Regards
Rama.
 
October   17, 2008 - 8:29 pm UTC 
 
currently, you'd have to use dynamic sql or hide the WITH construct in a view 
 
 
Thanks, Tom. I thought I was doing something wrong so I thought I will check with you.
rama, October   20, 2008 - 4:07 pm UTC
 
 
Regards 
 
result set not materialized with rownum
James Su, February  07, 2009 - 5:53 pm UTC
 
 
Dear Tom,
version 10.2.0.1.0
create table test (id number);
insert into test values (1);
insert into test values (2);
insert into test values (3);
select * 
  from test
      ,(select dbms_random.value rnd, rownum rn from dual);
       ID        RND         RN
--------- ---------- ----------
        1 .587863701          1
        2 .454931209          1
        3 .454931209          1
You can see dbms_random.value was called twice, even rownum can't stop it.
But this happens in sqlplus only. When I tested it in TOAD it was fine.
 
February  09, 2009 - 6:15 pm UTC 
 
use with
with data as (select ... from dual)
select * from test, data; 
 
 
with doesn't work
James Su, February  09, 2009 - 9:00 pm UTC
 
 
Dear Tom,
I tried with subquery but it still doesn't work:
with data as (select dbms_random.value rnd, rownum rn from dual) 
select * from test, data;
        ID        RND         RN
---------- ---------- ----------
         1 .681603252          1
         2 .107975268          1
         3 .107975268          1
 
February  10, 2009 - 7:03 am UTC 
 
materialize hint would probably do it, but as you can see, it is not going to be "assured" 
 
 
why does client matter
James Su, February  10, 2009 - 11:18 am UTC
 
 
hi Tom,
But when I tried it in other clients, like:
PLSQL Developer, TOAD, and iSqlplus, it could not be reproduced (resultset was materialized). 
Why does client matter?
CTAS and INSERT ... SELECT ... are both fine:
CREATE TABLE test2 AS
with data as (select dbms_random.value rnd, rownum rn from dual)
 select * from test, data;
SELECT * FROM TEST2;
        ID        RND         RN
---------- ---------- ----------
         1 ,888141391          1
         2 ,888141391          1
         3 ,888141391          1
         
DELETE TEST2;
INSERT INTO TEST2
with data as (select dbms_random.value rnd, rownum rn from dual)
 select * from test, data;
SELECT * FROM TEST2;
        ID        RND         RN
---------- ---------- ----------
         1  ,39375538          1
         2  ,39375538          1
         3  ,39375538          1
So it happens only in select in SQLPLUS, that looks weird to me. 
February  11, 2009 - 9:23 am UTC 
 
look at the plans and they'll be different.
you are relying on a specific plan to happen, and that is never assured.
In this case, you will absolutely want to
a) call dbms_random.value
b) bind that value 
if you need it to be predicable 
 
 
Regarding hints for inline views
Gaurav, February  24, 2009 - 8:55 am UTC
 
 
Hello Tom
I was looking around for an answer to my doubts, but didn't find it anywhere, and so here I am -with some trepidation and hoping this doesn't qualify as a "New question".
My situation is ...
I need to tune a query running into hundreds of lines containing more than a hundred GROUP BY expressions among other things. Here two fact tables (40m rows each, one partitioned) are joined , along with 8 dimension tables of varying sizes, some self-joined. All these are joined to two views, the two views again being joins involving two other fact tables and other smaller tables. The two views  return 26 million rows each. The query returns millions of rows and there are LOTS of full-table scans.
I am NOT allowed to create any indexes or to materialize anything or make any substantial changes. So far only parallelizing has achieved some results.
My question is:
(1) Since, in my humble understanding, Oracle merges the text of the views into the main body of the query, can I place a parallel(table_name) hint with the table_name of the
fact tables in the two views? These two tables are not referenced in the main query.
(2)In case that doesn't work, would it work to use the view text as inline views in the main query, with the parallel hints placed in the main body?
(3)Similar to (2) above, I can possibly replace the two views in the with clause. However, can I use the parallel hint on the fact tables in the with clauses ? 
In case - in 2 or 3 above, parallel hints can be placed on the tables in the with clauses or inline views - where should these be placed - in the views / inline views themselves or in the body of the main query after the SELECT statement?
It would help if you give some examples or some references. 
 
with clause
A reader, April     24, 2009 - 10:04 pm UTC
 
 
Hi Tom,
If a front end BI tools generates a query with WITH clause, and there is a Materialized view on the base table in the oracle db (10gR1). Will Oracle use MV with query rewrite enabled?
Thanks 
April     27, 2009 - 1:32 pm UTC 
 
the front end BI tool is not relevant... The question is
can a query with WITH subquerying factoring be rewritten to use a materialized view?
Answer: absolutely yes
ops$tkyte%ORA10GR2> create table t1
  2  as
  3  select *
  4    from all_objects;
Table created.
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> create materialized view mv1
  2  enable query rewrite
  3  as
  4  select distinct owner
  5    from t1
  6  /
Materialized view created.
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> create table t2
  2  as
  3  select *
  4    from t1;
Table created.
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> create materialized view mv2
  2  enable query rewrite
  3  as
  4  select owner, count(*)
  5    from t2
  6   group by owner
  7  /
Materialized view created.
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> begin
  2          dbms_stats.gather_table_stats( user, 'T1' );
  3          dbms_stats.gather_table_stats( user, 'T2' );
  4          dbms_stats.gather_table_stats( user, 'MV1' );
  5          dbms_stats.gather_table_stats( user, 'MV2' );
  6  end;
  7  /
PL/SQL procedure successfully completed.
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> @at
ops$tkyte%ORA10GR2> column PLAN_TABLE_OUTPUT format a72 truncate
ops$tkyte%ORA10GR2> set autotrace traceonly explain
ops$tkyte%ORA10GR2> with data
  2  as
  3  (select distinct owner
  4     from t1
  5    where owner like '%X%' )
  6  select count(*)
  7    from t2
  8   where owner in ( select * from data )
  9  /
Execution Plan
----------------------------------------------------------
Plan hash value: 289368505
------------------------------------------------------------------------
| Id  | Operation                      | Name | Rows  | Bytes | Cost (%C
------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |      |     1 |    19 |     5  (
|   1 |  SORT AGGREGATE                |      |     1 |    19 |
|*  2 |   HASH JOIN SEMI               |      |     2 |    38 |     5  (
|*  3 |    MAT_VIEW REWRITE ACCESS FULL| MV2  |     2 |    24 |     2
|*  4 |    MAT_VIEW REWRITE ACCESS FULL| MV1  |     2 |    14 |     2
------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("MV2"."OWNER"="MV1"."OWNER")
   3 - filter("MV2"."OWNER" LIKE '%X%')
   4 - filter("MV1"."OWNER" LIKE '%X%')
ops$tkyte%ORA10GR2> set autotrace off
 
 
 
How was 75%++ reducation calculated
A reader, May       28, 2009 - 11:14 am UTC
 
 
In an early exampele in this thread, you said "See the 75%++ reduction in LIO's -- we didn't have to distinct owner from all_users two times.".  My question is how this 75% reduction was calculated. 
May       28, 2009 - 2:04 pm UTC 
 
sorry, I think
.. See the 75%++ reduction in LIO's  ...
should have read
see how the one query uses less than 75% the LIO of the other
or
see how we reduced the amount of logical IO to less than 75% of what it otherwise would be. 
 
 
open cursor with clause
Samy, May       29, 2009 - 8:11 am UTC
 
 
Hi TOM,
can we use Open v_Cursor For With clause in PLSQL Block.
i tried it but was giving error. 
Procedure SearchEmpDtls(vEmpno Varchar(10),io_Cursor in out t_cursor)
is
v_Cursor t_cursor;
Begin
Open v_Cursor For
With Empdtls as ( Select Empno , Ename , Hiredate From EMP where Empno = vEmpno )
Select Empno , Ename , Hiredate From Empdtls Where 
Hiredate in ( Select Max(Hiredate) from Empdtls );
 io_cursor:=v_cursor;
End;
 
May       29, 2009 - 8:40 am UTC 
 
well, your code has a few mistakes
a) everyone knows empno is a number
b) you cannot have constrained types as parameters (varchar2(10) - not allowed, varchar2 - allowed)
c) t_cursor is not a type I know of
d) this obviously must be in a package
so, you give something that could never compile - even on a good day....
ops$tkyte%ORA9IR2> create table emp as select * from scott.emp;
Table created.
ops$tkyte%ORA9IR2>
ops$tkyte%ORA9IR2> create or replace package my_pkg
  2  as
  3          type t_cursor is ref cursor;
  4
  5          Procedure SearchEmpDtls(vEmpno number,io_Cursor in out t_cursor);
  6  end;
  7  /
Package created.
ops$tkyte%ORA9IR2> show errors
No errors.
ops$tkyte%ORA9IR2>
ops$tkyte%ORA9IR2>
ops$tkyte%ORA9IR2> create or replace package body my_pkg
  2  as
  3
  4  Procedure SearchEmpDtls(vEmpno number,io_Cursor in out t_cursor)
  5  is
  6          v_Cursor t_cursor;
  7  Begin
  8
  9  Open v_Cursor For
 10  With Empdtls as ( Select Empno , Ename , Hiredate From EMP where Empno = vEmpno )
 11  Select Empno , Ename , Hiredate From Empdtls Where
 12  Hiredate in ( Select Max(Hiredate) from Empdtls );
 13
 14   io_cursor:=v_cursor;
 15
 16  End;
 17  end;
 18  /
Package body created.
ops$tkyte%ORA9IR2>
ops$tkyte%ORA9IR2> variable x refcursor
ops$tkyte%ORA9IR2> exec my_pkg.searchempdtls( 7788, :x )
PL/SQL procedure successfully completed.
ops$tkyte%ORA9IR2> print x
     EMPNO ENAME      HIREDATE
---------- ---------- ---------
      7788 SCOTT      09-DEC-82
 
 
 
WITH Clause issue
TAD259, June      16, 2009 - 2:16 pm UTC
 
 
In 10gR2 on AIX, why does this work . . .
SELECT AA.AccrualValue.DEMANDINTERESTEARNED
  FROM ( 
       SELECT DemandAccrualValue.GetAccrualValue(1000041, NULL, DATE '2010-03-31') AccrualValue
         FROM DUAL
       ) AA
where this does not . . .
  WITH AA
    AS (
       SELECT DemandAccrualValue.GetAccrualValue(1000041, NULL, DATE '2010-03-31') AS AccrualValue
         FROM DUAL
       )
SELECT AA.AccrualValue.DEMANDINTERESTEARNED
  FROM AA
failing with "AA"."ACCRUALVALUE"."DEMANDINTERESTEARNED": invalid identifier. 
DemandAccrualValue is, of course, a user-defined type.
Sincere thanks in anticipation.
 
June      16, 2009 - 2:57 pm UTC 
 
it is of course a udt I don't have :(
as if your function :( :(
my testing does not show what you say you see - so, given no test case - I have no answer other than "you might have typed in something wrong"
ops$tkyte%ORA10GR2> drop type myScalarType;
Type dropped.
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> create or replace type myScalarType
  2  as object
  3  ( x int,
  4    y date,
  5    z varchar2(30)
  6  )
  7  /
Type created.
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> select *
  2    from (select myScalarType( 1, sysdate, 'hello world' ) as x from dual ) d
  3  /
X(X, Y, Z)
-------------------------------------------------------------------------------
MYSCALARTYPE(1, '16-JUN-09', 'hello world')
ops$tkyte%ORA10GR2> with data
  2  as
  3  (select myScalarType( 1, sysdate, 'hello world' ) as x from dual )
  4  select *
  5    from data d
  6  /
X(X, Y, Z)
-------------------------------------------------------------------------------
MYSCALARTYPE(1, '16-JUN-09', 'hello world')
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> select d.x.y
  2    from (select myScalarType( 1, sysdate, 'hello world' ) as x from dual ) d
  3  /
X.Y
---------
16-JUN-09
ops$tkyte%ORA10GR2> with data
  2  as
  3  (select myScalarType( 1, sysdate, 'hello world' ) as x from dual )
  4  select d.x.y
  5    from data d
  6  /
X.Y
---------
16-JUN-09
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> create or replace function f return myScalarType
  2  as
  3  begin
  4          return myScalarType( 1, sysdate, 'hello world' );
  5  end;
  6  /
Function created.
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> select d.x.y
  2    from (select f as x from dual ) d
  3  /
X.Y
---------
16-JUN-09
ops$tkyte%ORA10GR2> with data
  2  as
  3  (select f as x from dual )
  4  select d.x.y
  5    from data d
  6  /
X.Y
---------
16-JUN-09
 
 
 
To TAD259 from Toronto
Kim Berg Hansen, June      18, 2009 - 9:35 am UTC
 
 
You write:
WITH AA
    AS (
       SELECT DemandAccrualValue.GetAccrualValue(1000041, NULL, DATE '2010-03-31') AS AccrualValue
         FROM DUAL
       )
SELECT AA.AccrualValue.DEMANDINTERESTEARNED
  FROM AAYou need to "alias the alias" for object referencing to work:
WITH AA
    AS (
       SELECT DemandAccrualValue.GetAccrualValue(1000041, NULL, DATE '2010-03-31') AS AccrualValue
         FROM DUAL
       )
SELECT aa_alias.AccrualValue.DEMANDINTERESTEARNED
  FROM AA aa_aliasJust as Tom did in his example where he aliases "data" as "d".
Hope that helps :-) 
June      18, 2009 - 3:23 pm UTC 
 
doh, of course :)
thanks! 
 
 
"UNION ALL" inside WITH or Inline View
Parthiban Nagarajan, June      26, 2009 - 7:43 am UTC
 
 
Hi Tom ...
Let us have the sample data as follows ...
create table a_tabs as select table_name from user_tables where table_name like 'A%';
create table b_tabs as select table_name from user_tables where table_name like 'B%';
-- The Parameter ...
var p_starts_with varchar2(1)
exec :p_starts_with := 'A';
-- For the query ...
select * from (
select 'A' starts_with, table_name from a_tabs
 union all
select 'B' starts_with, table_name from b_tabs
) where starts_with = :p_starts_with ;
... because I'm using "UNION ALL" instead of "UNION", I believe, Oracle will select the records from "a_tabs" and won't go for "b_tabs" at all ...
Am I right ... ?
At least, is it right when I use the parameter value directly instead of using the bind variable ?
Thanks in Advance ...
-- The clean-up
drop table a_tabs;
drop table b_tabs;
 
June      26, 2009 - 11:03 am UTC 
 
why didn't you test it using tkprof and see?  It is what I would have to do to "show" you what happens.
ops$tkyte%ORA10GR2> select * from (
  2  select 'A' starts_with, table_name from a_tabs
  3   union all
  4   select 'B' starts_with, table_name from b_tabs
  5   ) where starts_with = :p_starts_with ;
no rows selected
ops$tkyte%ORA10GR2> select * from table(dbms_xplan.display_cursor);
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------
SQL_ID  4yygg48fpc7v2, child number 0
-------------------------------------
select * from ( select 'A' starts_with, table_name from a_tabs  union
all  select 'B' starts_with, table_name from b_tabs  ) where
starts_with = :p_starts_with
Plan hash value: 4062280091
-------------------------------------------------------------------------------
| Id  | Operation            | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |        |       |       |     4 (100)|          |
|   1 |  VIEW                |        |     2 |    40 |     4   (0)| 00:00:01 |
|   2 |   UNION-ALL          |        |       |       |            |          |
|*  3 |    FILTER            |        |       |       |            |          |
|   4 |     TABLE ACCESS FULL| A_TABS |     1 |    17 |     2   (0)| 00:00:01 |
|*  5 |    FILTER            |        |       |       |            |          |
|   6 |     TABLE ACCESS FULL| B_TABS |     1 |    17 |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - filter('A'=:P_STARTS_WITH)
   5 - filter('B'=:P_STARTS_WITH)
Note
-----
   - dynamic sampling used for this statement
30 rows selected.
but yes, the file steps on lines 3 and 5 would cause one or the other or neither table to be scanned - depending on the value of the bind variable. 
 
 
Not materializing?
Lise, August    12, 2009 - 7:09 am UTC
 
 
Hi,
I have a query that uses a simple WITH subquery written in 9iR2.
It looks like this:
WITH policy AS (SELECT b.col1 policy_reference
                FROM table_a b
                WHERE b.col1 BETWEEN 1 AND 100000
                )           
SELECT *
FROM   (SELECT a.col1
              ,a.col2
        FROM   table_b a
              ,table_c c
              ,policy
        WHERE  a.col1 = policy.policy_reference
        AND c.col2 = a.col2
        UNION ALL
        SELECT c.col1
              ,c.col2
        FROM   table_c c
              ,policy
              ,table_b b
        WHERE  c.col1 = policy.policy_reference
        AND b.col2 = c.col2) increments
      ,(SELECT a.col1
              ,SUM(a.col1) tot
              ,a.col2
        FROM   table_b a
              ,policy
        WHERE  a.col1 = policy.policy_reference
        GROUP  BY a.col1
                 ,a.col2) employer_grsprmamt
WHERE  employer_grsprmamt.col1 = increments.col1
AND    employer_grsprmamt.col2 = increments.col2
If I try to run this it will work. However, with the business tables and data I am using, I get an ORA-00942 (table or view does not exist).
If I change the UNION ALL to UNION it works, or if I only include one of the statements in the main WHERE clause it works. 
I have added in the materialize hint, but since I cannot even create the explain plan, I do not know if it is used. 
I can only assume that it is not using it, and hence causing the ORA error when the WITH subquery is referenced in the inline views?
I have tried the same query in 10g and it seems to work fine.
Any suggestions would be greatly appreciated. 
August    13, 2009 - 11:47 am UTC 
 
I'd need your help to reproduce
as in "the tables" 
 
 
Metalink bug
Lise, September 02, 2009 - 5:07 am UTC
 
 
Hi,
Please ignore my previous question. I found on metalink that this is a bug that has been fixed in release 10g.
Thanks
 
 
DML using subquery factoring
Ravi, November  13, 2009 - 4:28 pm UTC
 
 
Hi Tom,
Is there any limitation for UPDATE using WITH clause?
Following construct works fine (single column update)
UPDATE emp e
SET    sal = ( WITH q1 AS (
                        SELECT empno,sal,deptno
                         FROM emp
                        ),
                     q2 AS (
                        SELECT deptno from dept)
              SELECT sal + 100
               FROM q1,q2 
              where q1.deptno=q2.deptno
                AND e.empno=q1.empno
               
               )
But the following query gives the error (multi column update)
UPDATE emp e
SET    (sal,comm) = ( WITH q1 AS (
                        SELECT empno,sal,deptno
                         FROM emp
                        ),
                     q2 AS (
                        SELECT deptno from dept)
              SELECT sal + 100, 100
               FROM q1,q2 
              where q1.deptno=q2.deptno
                AND e.empno=q1.empno
               
               )
ORA-01767: UPDATE ... SET expression must be a subquery
Thanks,
Ravi
 
November  15, 2009 - 3:16 pm UTC 
 
ops$tkyte%ORA10GR2> UPDATE emp e
  2  SET    (sal,comm) = ( WITH q1 AS (
  3                          SELECT empno,sal,deptno
  4                           FROM emp
  5                          ),
  6                       q2 AS (
  7                          SELECT deptno from dept)
  8                SELECT sal + 100 sal, 100 comm
  9                 FROM q1,q2
 10                where q1.deptno=q2.deptno
 11                  AND e.empno=q1.empno)
 12  /
SET    (sal,comm) = ( WITH q1 AS (
                    *
ERROR at line 2:
ORA-01767: UPDATE ... SET expression must be a subquery
ops$tkyte%ORA10GR2> UPDATE emp e
  2  SET    (sal,comm) = (
  3                SELECT sal + 100 sal, 100 comm
  4                 FROM  dual )
  5  /
14 rows updated.
I would concur, looks like a parser bug, can you file with support?  Let me know, if not, I will 
 
 
 
no metalink support
Ravi, November  15, 2009 - 4:47 pm UTC
 
 
Hi Tom,
We dont have metalink account. If possible, could you please file a bug.
Thanks. 
November  23, 2009 - 8:58 am UTC 
 
filed bug 9147631  
 
 
WITH Clause within a View
RAmchandra Joshi, January   21, 2010 - 8:38 am UTC
 
 
Hi Tom,
Can you please elaborate as to how oracle processes a WITH clause which is defined in a view ?
I'm currently facing a strange problem where my view definition contains a WITH clause query and I'm using this view with another WITH clause and a UNION ALL operation.
So the query format is something like 
WITH v_temp as ( SELECT * FROM view_with_with_clause )
select * from v_temp
union all
select * from v_temp
But to my surprise the results are different when I run this query.
So just wanted to understand how oracle actually processes a WITH clause withing view ?
It must be sounding weird to you currently but I'm preparing a test case for you to reproduce.
Till then if you can throw some light on the processing , It would be really helpful.
Thanks in advance,
Ram. 
January   21, 2010 - 10:36 am UTC 
 
give us a for example.  a test case.  
... But to my surprise the results are different when I run this query. ...
different than WHAT exactly?????
if you want to see how your query is probably getting processed, run an explain plan on it. 
 
 
different behaviour of with-clause with and without materialize-hint
Sokrates, March     29, 2010 - 3:53 am UTC
 
 
Tom, I noticed the following difference in behaviour of with-clause, reproduced on 11.2.0.1 and 10.2.0.2
Is it a bug or intended behaviour ?
(I vote for the 1st, as I think, the result of a query should not depend upon a hint)
11.2.0.1 > create function fdummy return varchar2 is
  2  begin
  3     return 'X';
  4  end;
  5  /
Function created.
11.2.0.1 > with a as (select fdummy b from dual)
  2  select count(*), b
  3  from a, dual
  4  where dummy = b;
  COUNT(*)
----------
B
---------------------------------------------------------------------------------------
         1
X
11.2.0.1 > edit
Wrote file afiedt.buf
  1  with a as (select /*+materialize */ fdummy b from dual)
  2  select count(*), b
  3  from a, dual
  4* where dummy = b
11.2.0.1 > /
select count(*), b
                 *
ERROR at line 2:
ORA-00937: not a single-group group function
 
 
April     05, 2010 - 9:34 am UTC 
 
technically - I think the first one is a bug, it should demand a group by as well.  But I see what they did with the rewrite, they merged away the with subquery and flattened everything .
You do have to be careful when calling non-deterministic functions from SQL - can you imagine what would happen if all references to b are replaced with fdummy (which they were) and treated as non-constant instead of constant - and fdummy did return different values... 
 
 
Force inline behaviour when using WITH clause
Dave, May       19, 2010 - 6:31 am UTC
 
 
Hi Tom,
Refactored sub queries are fantastic for performance and make some SQL sooooo much easier to read and maintain, however there are conditions where you know you would not want the CBO to materialize the subquery in the WITH clause.
So is there a technique (other than inlining the queries in the SQL again or using the undocumented /*+ inline */ hint) to force the inline rather than materialzed use of the sub queries?
Or is using the WITH clause with the only objective being to make the SQL more maintainable a bad idea?
Many thanks 
May       24, 2010 - 10:38 am UTC 
 
"WITH" does NOT mandate "materialize".  It will inline queries whenever it makes sense to do so. 
 
 
Re: Force inline behaviour when using WITH clause
Dave, May       20, 2010 - 3:57 am UTC
 
 
Ignore me, i'll somewhat obviously need to create a view. Sometimes I find myself looking for problems where there are none, maybe I haven't enought to do ;) 
 
With clause performance problem
Praveen, May       21, 2010 - 2:19 pm UTC
 
 
Hi Tom,
One of my query which contains with clause is taking less than 1 sec in
dev while in production it is taking lot of time and consuming lot of
CPU memory. The amount of data is same in dev and prod.
Can you please let me know where iam going wrong in the below query :
SQL> select 'p',count(*) from sof_products
  2  union all
  3  select 's',count(*) from sof
  4  union all
  5  select 'w',count(*) from sof_work_unit
  6  union all
  7  select 'm',count(*) from products_master
  8  union all
  9  select 'c', count(*) from status
10  union all
11  select 'q',count(*) from TED_QUEUE_STATUS
12  union all
13  select 't', count(*) from prod_tab where category in
('P','C');
'   COUNT(*)
- ----------
p    1694197
s     860518
w     110632
m        243
c         23
q         79
t        126
7 rows selected.
Elapsed: 00:00:00.56
SQL> set autotrace traceonly explain
SQL> variable p_category number;
SQL> variable p_display_ref_no varchar2;
SQL> variable p_customer varchar2;
SQL> variable p_request_id varchar2
SQL> variable p_status varchar2;
SQL> variable p_user_id1 varchar2;
SQL> variable p_subgrp varchar2;
SQL> SELECT  orderby_assigned ,  orderby_submitted_date ,
display_ref_no , ref_no ,  Dcmfile_existing_flag ,
  2          status_code ,  product_id ,  sub_status_code ,
company_name ,  sof_submit_date ,  created_by , assigned_date ,
  3    created_by_user_name ,status_desc, MASTERPRODUCT_ID,
CREATED_BY_SUBGROUP, SEND_TO_SUBGROUP,
  4          WORK_REQUEST_SENT_DATE, FILE_UPLOADED, STATUS_DATE,
CREATED_DATE, WORK_UNIT_ID, WORKUNIT, ASSIGNED,
  5         INITIAL_DESCRIPTION, NICKNAME, CATEGORY
  6  FROM (
  7  WITH TEMP AS (
  8  SELECT /*+ MATERIALIZE */
  9        UNIQUE
10      E.orderby_assigned , E.orderby_submitted_date ,
E.display_ref_no , E.dcmfile_existing_flag , E.COMPANY_NAME,
11      E.CREATED_BY_USER_NAME,
12
DECODE(E.CATEGORY,'P',E.PRODUCT_ID||'ZZZZZZZZZZ','C',E.MASTERPRODUCT_ID)
MASTERPRODUCT_ID,
13      E.sof_submit_date,  A.REF_NO, A.SEND_TO_SUBGROUP,
A.WORK_REQUEST_SENT_DATE, A.FILE_UPLOADED, A.STATUS_DATE,
14      A.CREATED_BY, A.CREATED_DATE, A.WORK_UNIT_ID, A.RELATED_ITEM,
15      DECODE(INSTR(SUBSTR(A.WORK_UNIT_ID,
LENGTH(A.RELATED_ITEM)+1),'.'),
16      0, LPAD(SUBSTR(A.WORK_UNIT_ID,LENGTH(A.RELATED_ITEM)+1),3,'0'),
17      2, '00'||SUBSTR(REPLACE(A.WORK_UNIT_ID, '.10',
'.91'),LENGTH(A.RELATED_ITEM)+1),
18      3, '0'||SUBSTR(REPLACE(A.WORK_UNIT_ID, '.10', '.91'),
LENGTH(A.RELATED_ITEM)+1),
19      4, SUBSTR(REPLACE(A.WORK_UNIT_ID, '.10', '.91'),
LENGTH(A.RELATED_ITEM)+1)) AS WORKUNIT,
20      A.ASSIGNED_DATE, A.ASSIGNED, A.STATUS_CODE, A.SUB_STATUS_CODE,
A.CREATED_BY_SUBGROUP, A.INITIAL_DESCRIPTION,
21      B.STATUS_DESC, A.NICKNAME, A.PARENT_REJECTED,
DECODE(E.CATEGORY,'P','PW','C','CW') CATEGORY
22  FROM STATUS B,
23       PRODUCTS_MASTER M,
24       SOF_PRODUCTS C,
25       SOF_WORK_UNIT A,
26    TED_QUEUE_STATUS Q,
27    PROD_TAB E
28  WHERE A.STATUS_CODE=B.STATUS_CODE
29  AND C.PRODUCT_ID = M.PRODUCT_ID
30  AND C.STATUS_CODE IS NOT NULL
31  AND Q.CATEGORY = (CASE
32                         WHEN :P_CATEGORY <> 0 THEN
33          'AS'
34          WHEN :P_DISPLAY_REF_NO IS NOT NULL THEN
35          'AS'
36          WHEN :P_CUSTOMER IS NOT NULL THEN
37          'AS'
38          WHEN :P_REQUEST_ID IS NOT NULL THEN
39          'AS'
40          WHEN :P_STATUS IS NOT NULL THEN
41          'AS'
42        WHEN :P_USER_ID1 IS NOT NULL THEN
43          'AS'
44          ELSE
45          'A'
46          END
47          )
48          AND
DECODE(:P_STATUS,NULL,'X',DECODE(:p_category,0,A.STATUS_CODE||A.SUB_STAT
US_CODE,1,'X',2,'X',3,
49
A.STATUS_CODE||A.SUB_STATUS_CODE,4,'X',5,'X',6,A.STATUS_CODE||A.SUB_STAT
US_CODE,7,A.STATUS_CODE||A.SUB_STATUS_CODE,
50  8,A.STATUS_CODE||A.SUB_STATUS_CODE))
51     IN (select NVL(COLUMN_VALUE,'X') from TABLE(cast(
aes_ted_work_items_pkg.str2tbl(:P_STATUS ) as mytableType)))
52  AND A.RELATED_ITEM = C.PRODUCT_ID
53  AND A.REF_NO = C.REF_NO
54  AND Q.STATUS_CODE  = A.STATUS_CODE
55  AND ((A.SEND_TO_SUBGROUP=:P_SUBGRP
56        AND A.STATUS_CODE NOT IN ('DEC','RJT')
57    )
58        OR A.CREATED_BY_SUBGROUP=:P_SUBGRP
59        )
60  AND NVL(A.ASSIGNED,'X') =
DECODE(:P_USER_ID1,NULL,NVL(A.ASSIGNED,'X'),:P_USER_ID1)
61  AND A.REF_NO= E.REF_NO
62  AND A.RELATED_ITEM = E.PRODUCT_id
63  AND E.CATEGORY IN ('P','C')
64  )
65  SELECT UNIQUE
66         T.orderby_assigned , T.orderby_submitted_date ,
T.display_ref_no , T.dcmfile_existing_flag , T.COMPANY_NAME,
67      T.CREATED_BY_USER_NAME, T.MASTERPRODUCT_ID, T.sof_submit_date,
T.REF_NO,  T.SEND_TO_SUBGROUP,
68      T.WORK_REQUEST_SENT_DATE, T.FILE_UPLOADED, T.STATUS_DATE,
T.CREATED_BY, T.CREATED_DATE, T.WORK_UNIT_ID,
69      T.RELATED_ITEM PRODUCT_ID, T.WORKUNIT, T.ASSIGNED_DATE,
T.ASSIGNED, T.STATUS_CODE, T.SUB_STATUS_CODE,
70      T.CREATED_BY_SUBGROUP, T.INITIAL_DESCRIPTION, T.STATUS_DESC,
T.NICKNAME, T.CATEGORY
71  FROM TEMP T
72  UNION ALL
73  SELECT UNIQUE
74         E.orderby_assigned , E.orderby_submitted_date ,
E.display_ref_no , E.dcmfile_existing_flag , E.COMPANY_NAME,
75      E.CREATED_BY_USER_NAME,
76
DECODE(E.CATEGORY,'P',E.PRODUCT_ID||'ZZZZZZZZZZ','C',E.MASTERPRODUCT_ID)
MASTERPRODUCT_ID,
77      E.sof_submit_date, A.REF_NO, A.SEND_TO_SUBGROUP,
A.WORK_REQUEST_SENT_DATE, A.FILE_UPLOADED, A.STATUS_DATE,
78      A.CREATED_BY, A.CREATED_DATE, A.WORK_UNIT_ID, A.RELATED_ITEM,
79      DECODE(INSTR(SUBSTR(A.WORK_UNIT_ID,
LENGTH(A.RELATED_ITEM)+1),'.'),
80      0, LPAD(SUBSTR(A.WORK_UNIT_ID,
LENGTH(A.RELATED_ITEM)+1),3,'0'),
81      2, '00'||SUBSTR(REPLACE(A.WORK_UNIT_ID, '.10', '.91'),
LENGTH(A.RELATED_ITEM)+1),
82      3, '0'||SUBSTR(REPLACE(A.WORK_UNIT_ID, '.10', '.91'),
LENGTH(A.RELATED_ITEM)+1),
83      4, SUBSTR(REPLACE(A.WORK_UNIT_ID, '.10', '.91'),
LENGTH(A.RELATED_ITEM)+1)) AS WORKUNIT,
84      A.ASSIGNED_DATE, A.ASSIGNED, A.STATUS_CODE, A.SUB_STATUS_CODE,
A.CREATED_BY_SUBGROUP, A.INITIAL_DESCRIPTION,
85      B.STATUS_DESC,A.NICKNAME, DECODE(E.CATEGORY,'P','PW','C','CW')
CATEGORY
86  FROM SOF_WORK_UNIT C,
87       STATUS B,
88    SOF_WORK_UNIT A,
89    PROD_TAB E
90  WHERE A.REF_NO=C.REF_NO
91  AND A.RELATED_ITEM=C.RELATED_ITEM
92  AND A.STATUS_CODE=B.STATUS_CODE
93  AND A.WORK_UNIT_ID IN
94  ( C.PARENT_WORK_UNIT,SUBSTR(C.PARENT_WORK_UNIT, 0,
INSTR(C.PARENT_WORK_UNIT, '.', LENGTH(C.RELATED_ITEM), 1)-1))
95  AND (C.REF_NO, C.WORK_UNIT_ID, C.RELATED_ITEM) IN
96   (SELECT DISTINCT T.REF_NO,
97          T.WORK_UNIT_ID,
98     T.RELATED_ITEM
99    FROM TEMP T
100    WHERE T.REF_NO = C.REF_NO
101    AND T.WORK_UNIT_ID = C.WORK_UNIT_ID
102    AND T.RELATED_ITEM = C.RELATED_ITEM
103         )
104  AND A.REF_NO= E.REF_NO
105  AND A.RELATED_ITEM = E.PRODUCT_id
106  AND E.CATEGORY IN ('P','C')
107  );
Elapsed: 00:00:00.62
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=15 Card=2 Bytes=10
          92)
   1    0   VIEW (Cost=15 Card=2 Bytes=1092)
   2    1     TEMP TABLE TRANSFORMATION
   3    2       LOAD AS SELECT
   4    3         SORT (UNIQUE) (Cost=187 Card=1 Bytes=295)
   5    4           CONCATENATION
   6    5             NESTED LOOPS (Cost=143 Card=1 Bytes=295)
   7    6               NESTED LOOPS (Cost=143 Card=1 Bytes=287)
   8    7                 HASH JOIN (Cost=141 Card=1 Bytes=269)
   9    8                   NESTED LOOPS (Cost=121 Card=1 Bytes=267)
  10    9                     NESTED LOOPS (Cost=120 Card=1 Bytes=246)
  11   10                       NESTED LOOPS (Cost=120 Card=1 Bytes=24
          0)
  12   11                         INLIST ITERATOR
  13   12                           TABLE ACCESS (BY INDEX ROWID) OF 'PROD_TAB' (TABLE (TEMP)) (Cost=7 Card=82 Bytes=12054)
  14   13                             INDEX (RANGE SCAN) OF 'X2PROD_TAB' (INDEX) (Cost=3 Card=33)
  15   11                         TABLE ACCESS (BY INDEX ROWID) OF 'SOF_WORK_UNIT' (TABLE) (Cost=2 Card=1 Bytes=93)
  16   15                           INDEX (RANGE SCAN) OF 'X1SOF_WORK_UNIT' (INDEX) (Cost=1 Card=1)
  17   10                       INDEX (RANGE SCAN) OF 'X1TED_QUEUE_STATUS' (INDEX) (Cost=0 Card=1 Bytes=6)
  18    9                     TABLE ACCESS (BY INDEX ROWID) OF 'STATUS' (TABLE) (Cost=1 Card=1 Bytes=21)
  19   18                       INDEX (UNIQUE SCAN) OF 'XPKSTATUS' (INDEX (UNIQUE)) (Cost=0 Card=1)
  20    8                   COLLECTION ITERATOR (PICKLER FETCH) OF 'STR2TBL' (PROCEDURE)
  21    7                 TABLE ACCESS (BY INDEX ROWID) OF 'SOF_PRODUCTS' (TABLE) (Cost=2 Card=1 Bytes=18)
  22   21                   INDEX (UNIQUE SCAN) OF 'XPKSOF_PRODUCTS' (INDEX (UNIQUE)) (Cost=1 Card=1)
  23    6               INDEX (UNIQUE SCAN) OF 'XPKPRODUCTS_MASTER' (INDEX (UNIQUE)) (Cost=0 Card=1 Bytes=8)
  24    5             HASH JOIN (Cost=42 Card=4 Bytes=1180)
  25   24               NESTED LOOPS (Cost=21 Card=1 Bytes=293)
  26   25                 NESTED LOOPS (Cost=21 Card=1 Bytes=285)
  27   26                   NESTED LOOPS (Cost=19 Card=1 Bytes=267)
  28   27                     NESTED LOOPS (Cost=18 Card=1 Bytes=246)
  29   28                       HASH JOIN (Cost=18 Card=1 Bytes=240)
  30   29                         TABLE ACCESS (BY INDEX ROWID) OF 'SOF_WORK_UNIT' (TABLE) (Cost=11 Card=17 Bytes=1581)
  31   30                           INDEX (RANGE SCAN) OF 'X5SOF_WORK_UNIT' (INDEX) (Cost=7 Card=18)
  32   29                         INLIST ITERATOR
  33   32                           TABLE ACCESS (BY INDEX ROWID) OF 'PROD_TAB' (TABLE (TEMP)) (Cost=7 Card=82 Bytes=12054)
  34   33                             INDEX (RANGE SCAN) OF 'X2PROD_TAB' (INDEX) (Cost=3 Card=33)
  35   28                       INDEX (RANGE SCAN) OF 'X1TED_QUEUE_STATUS' (INDEX) (Cost=0 Card=1 Bytes=6)
  36   27                     TABLE ACCESS (BY INDEX ROWID) OF 'STATUS' (TABLE) (Cost=1 Card=1 Bytes=21)
  37   36                       INDEX (UNIQUE SCAN) OF 'XPKSTATUS' (INDEX (UNIQUE)) (Cost=0 Card=1)
  38   26                   TABLE ACCESS (BY INDEX ROWID) OF 'SOF_PRODUCTS' (TABLE) (Cost=2 Card=1 Bytes=18)
  39   38                     INDEX (UNIQUE SCAN) OF 'XPKSOF_PRODUCTS' (INDEX (UNIQUE)) (Cost=1 Card=1)
  40   25                 INDEX (UNIQUE SCAN) OF 'XPKPRODUCTS_MASTER' (INDEX (UNIQUE)) (Cost=0 Card=1 Bytes=8)
  41   24               COLLECTION ITERATOR (PICKLER FETCH) OF 'STR2TBL' (PROCEDURE)
  42    2       UNION-ALL
  43   42         SORT (UNIQUE) (Cost=3 Card=1 Bytes=266)
  44   43           VIEW (Cost=2 Card=1 Bytes=266)
  45   44             TABLE ACCESS (FULL) OF 'SYS_TEMP_0FD9D6734_C7E3A1A6' (TABLE (TEMP)) (Cost=2 Card=1 Bytes=268)
  46   42         SORT (UNIQUE) (Cost=12 Card=1 Bytes=307)
  47   46           NESTED LOOPS (Cost=11 Card=1 Bytes=307)
  48   47             NESTED LOOPS (Cost=6 Card=1 Bytes=160)
  49   48               NESTED LOOPS (Cost=5 Card=1 Bytes=139)
  50   49                 NESTED LOOPS (Cost=3 Card=1 Bytes=48)
  51   50                   VIEW (Cost=2 Card=1 Bytes=23)
  52   51                     TABLE ACCESS (FULL) OF 'SYS_TEMP_0FD9D6734_C7E3A1A6' (TABLE (TEMP)) (Cost=2 Card=1 Bytes=268)
  53   50                   TABLE ACCESS (BY INDEX ROWID) OF 'SOF_WORK_UNIT' (TABLE) (Cost=1 Card=1 Bytes=25)
  54   53                     INDEX (UNIQUE SCAN) OF 'WORKUNIT_PK' (INDEX (UNIQUE)) (Cost=0 Card=1)
  55   49                 TABLE ACCESS (BY INDEX ROWID) OF 'SOF_WORK_UNIT' (TABLE) (Cost=2 Card=1 Bytes=91)
  56   55                   INDEX (RANGE SCAN) OF 'X1SOF_WORK_UNIT' (INDEX) (Cost=1 Card=1)
  57   48               TABLE ACCESS (BY INDEX ROWID) OF 'STATUS' (TABLE) (Cost=1 Card=1 Bytes=21)
  58   57                 INDEX (UNIQUE SCAN) OF 'XPKSTATUS' (INDEX (UNIQUE)) (Cost=0 Card=1)
  59   47             INLIST ITERATOR
  60   59               TABLE ACCESS (BY INDEX ROWID) OF 'PROD_TAB' (TABLE (TEMP)) (Cost=5 Card=1 Bytes=147)
  61   60                 INDEX (RANGE SCAN) OF 'X2PROD_TAB' (INDEX) (Cost=2 Card=2)   
The above explain plan was taken in dev environment.
Thanks,
Praveen
 
 
 
Is this a bug in your opinion?
Alien, August    20, 2010 - 3:40 pm UTC
 
 
Hi Tom,
this is in response to the question / answer from 10th april 2010. A similar issue came up recently. And I'm wondering if this is a bug, or if there is a legal explanation: 
create table t1 (num number);
insert into t1 values(1);
create table t2 (num number);
insert into t2 values(1);
with t as (select num as col from t1
           union all 
           select num from t1
           union all
           select num from t1)
select num from t2
where 1 >= any (select col from t);
1
-
1
1
1
Expected was just one row. On the other hand it's arguable that the any (select ...) transforms to a join. Your opinion would be appreciated.
 
August    25, 2010 - 12:47 pm UTC 
 
definitely a bug.  do you have access to support to file it - you have the perfect test case. 
 
 
Not a bug?
Michel Cadot, August    25, 2010 - 12:56 pm UTC
 
 
August    26, 2010 - 12:54 pm UTC 
 
it is a bug, I don't know who they talked to - but it is a bug, it is clearly a bug - a first year sql programmer should be able to see that :) 
 
 
Bug?
Duke Ganote, August    25, 2010 - 12:58 pm UTC
 
 
Curious indeed.  I tried it in 10gR2 and 11gR2:
select * from v$version;
BANNER
--------------------------------------------------------------------------------
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
PL/SQL Release 10.2.0.4.0 - Production
CORE    10.2.0.4.0      Production
TNS for IBM/AIX RISC System/6000: Version 10.2.0.4.0 - Productio
NLSRTL Version 10.2.0.4.0 - Production
with
t1 as ( select 1 as num from dual )
,
t2 as ( select 1 as num from dual )
, t as (select num as col from t1
           union all
           select num from t1
           union all
           select num from t1)
select num from t2
where 1 >= any (select col from t);
  NUM
-----
    1
select * from v$version;
BANNER
-----------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
PL/SQL Release 11.2.0.1.0 - Production
CORE    11.2.0.1.0      Production
TNS for 64-bit Windows: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 - Production
with
t1 as ( select 1 as num from dual )
,
t2 as ( select 1 as num from dual )
, t as (select num as col from t1
           union all
           select num from t1
           union all
           select num from t1)
select num from t2
where 1 >= any (select col from t)
  NUM
-----
    1
    1
    1
 
 
Duke Ganote, August    25, 2010 - 3:08 pm UTC
 
 
Curious, indeed.  The test query:
with
"union all" as (
select 1 as col from dual union all
select 1 as col from dual union all
select 1 as col from dual
)
,
"connect by" AS (
select 1 as col from dual
connect by level <= 3
)
select count(*), 'union all' as src
  from dual
 where 1 >= any
(select col from "union all")
UNION ALL
select count(*), 'connect by'
  from dual
 where 1 >= any
(select col from "connect by")
union all
select col, 'union all source'
  from "union all"
union all
select col, 'connect by source'
  from "connect by"
The results in 11gR2:
COUNT(*) SRC
-------- -----------------
       3 union all
       3 connect by
       1 union all source
       1 union all source
       1 union all source
       1 connect by source
       1 connect by source
       1 connect by source
and in 10gR2:
COUNT(*) SRC
-------- -----------------
       1 union all
       3 connect by
       1 union all source
       1 union all source
       1 union all source
       1 connect by source
       1 connect by source
       1 connect by source
 
 
lovely bug 
Sokrates, August    26, 2010 - 2:00 am UTC
 
 
 
Bug!
Michel Cadot, August    26, 2010 - 2:04 am UTC
 
 
My title was deliberately provocative, it is obvious it is a bug.
How a restriction could return more rows than the source set?
It should be like to screw on to get more water/gas than the tank can contain.
Regards
Michel
 
 
electric bugaloo
Duke Ganote, August    26, 2010 - 9:33 am UTC
 
 
I have 9iR2 available too, and of course the results are different from either 10gR2 or 11gR2:
COUNT(*) SRC
-------- -----------------
       1 union all         -- matches 10gR2, differs from 11gR2
       1 connect by        -- different than 10gR2 and 11gR2
       1 union all source
       1 union all source
       1 union all source
       1 connect by source
       1 connect by source
       1 connect by source
BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.8.0 - 64bit Production
PL/SQL Release 9.2.0.8.0 - Production
CORE    9.2.0.8.0       Production
TNS for IBM/AIX RISC System/6000: Version 9.2.0.8.0 - Production
NLSRTL Version 9.2.0.8.0 - Production
for the prior query:
with
"union all" as (
select 1 as col from dual union all
select 1 as col from dual union all
select 1 as col from dual
)
,
"connect by" AS (
select 1 as col from dual
connect by level <= 3
)
select count(*), 'union all' as src
  from dual
 where 1 >= any
(select col from "union all")
UNION ALL
select count(*), 'connect by'
  from dual
 where 1 >= any
(select col from "connect by")
UNION ALL
select col, 'union all source'
  from "union all"
UNION ALL
select col, 'connect by source'
  from "connect by"
 
 
bug -> SR
Duke Ganote, August    26, 2010 - 3:01 pm UTC
 
 
Service Request #3-2056934461 
 
SR workaround
Duke Ganote, September 15, 2010 - 7:08 am UTC
 
 
From metalink:
<quote>
The observation remains same on 11.2 and 11.1. And the output is wrong on 11g as well as 10g.
As an workaround, please use the below parameter.
sql> alter session set optimizer_features_enable='9.2.0';
</quote>
Followed up by an email asking me "is the workaround acceptable ?"
I hardly know what to say. 
September 15, 2010 - 8:21 am UTC 
 
have you asked them for the P2 bug # that they filed for this error - if they haven't tell them to or let them know that I will (and be annoyed at them for not filing a bug for something so obvious) 
 
 
Not Subquery Factoring
Jonathan Lewis, September 16, 2010 - 2:28 pm UTC
 
 
 
P2
Duke Ganote, February  17, 2011 - 7:02 am UTC
 
 
Oracle Support - February 17, 2011 7:46:01 AM GMT-05:00 [Notes]
Generic Note
------------------------
Hi,
I am trying to create a testcase and shall update you soon.
DGANOTE@XXXX.COM - September 15, 2010 11:24:08 AM GMT-05:00 [Update from Customer]
Really, this is an unacceptable workaround. What is the P2 bug # that you filed for this error ?? 
February  17, 2011 - 11:29 am UTC 
 
huh? they *have* a small test case 
 
 
P-tooey
Duke Ganote, April     26, 2011 - 3:40 am UTC
 
 
Oracle Support - April 18, 2011 12:19:55 PM GMT-04:00 [Notes]
Generic Note
------------------------
Hi Duke,
I have filed a bug 12371236 for this issue.
I shall update you as soon as lI have more information from dev.
Oracle Support - April 19, 2011 12:42:26 PM GMT-04:00 [Notes]
Generic Note
------------------------
Hi Duke,
I got the below update from bde.
<<
Please get customer to apply latest patchset in windows and this issue should
go away.
>>
Please apply latest patchset (11.2.0.2) where the issue is resolved.
 
 
Why this scalar query gives ora-904?
wm_shi, September 14, 2011 - 1:49 am UTC
 
 
SELECT (SELECT *
        FROM   (SELECT dummy
                FROM   dual
                WHERE  a.owner = 'x'))
FROM   all_objects a; 
September 14, 2011 - 7:17 pm UTC 
 
because it is not a scalar subquery.
It is an inline view.
and scoping rules say "that goes one level down only" 
 
 
Differences with FGA of factored subqueries should be mentioned
Peter Maklary, September 22, 2011 - 3:18 am UTC
 
 
Hi,
in addition to the performance aspects of using with clause (subquery factoring), a very interesting issue with FGA (Fine Grained Auditing) should be mentioned:
a select like
with empx as (select * from scott.emp) select * from empx
is NOT audited by FGA although an auditing policy for scott.emp is enabled, and select * from scott.emp is audited
An inline view version of this query, e.g.
select * from (select * from scott.emp) 
is audited as expected
 
September 22, 2011 - 5:40 pm UTC 
 
I am evaluating this.... 
 
 
WATCH OUT - some SQL clients don't like 'WITH'
Kiwi Nick, November  16, 2011 - 2:04 am UTC
 
 
I had been attempting a WITH ... SELECT statement and getting zero rows from all the queries.
I was using PL/SQL Developer 7.1.5.1398 on Windows 7 connected to Oracle 10.2.0.1.0 (Linux) or alternatively Oracle 10.2.0.3.0 (Tru64).
When I swapped to using the SQL*Plus on the Tru64 box, the WITH ... SELECT statement worked as expected.
Example query
with aa_ale as
(
  select * from all_objects o
  where o.owner = 'SYS'
  and o.object_name between 'AA' and 'ALL'
)
select owner, object_name, object_id, object_type from aa_ale;
Sample result (the names of objects alphabetically between 'A' and 'ALL')
OWNER   OBJECT_NAME    OBJECT_ID OBJECT_TYPE
------- ------------- ---------- -------------
SYS     ACCESS$               93 TABLE
SYS     ADMIN_DIR          46164 DIRECTORY
SYS     AGGXMLIMP           5930 TYPE
SYS     AGGXQIMP            5931 TYPE
SYS     ALERT_QT            8786 TABLE
SYS     ALERT_TYPE          8784 TYPE
but in PL/SQL Developer I was getting no rows. Just another thing to watch out for. 
 
Some SQL clients don't like 'WITH' - WORKAROUND
Kiwi Nick, November  16, 2011 - 5:56 pm UTC
 
 
Ok, this works in PL/SQL Developer.
select * from
(
  with aa_ale as
  (
    select * from all_objects o
    where o.owner = 'SYS'
    and o.object_name between 'AA' and 'ALL'
  )
  select owner, object_name, object_id, object_type from aa_ale
)Basically, I turned the query into a sub-query wrapped in 
SELECT * FROM (...)It might also help with any other strangeness (such as experienced by "A Reader" at February 28, 2003 - 9am). 
 
A reader, June      25, 2012 - 1:02 am UTC
 
 
 
 
Followup   November 23, 2009 - 8am Central time zone:  filed bug 9147631 
Spur, February  21, 2013 - 9:31 pm UTC
 
 
Tom - Do you know if the bug 9147631 that you filed earlier in this thread (November 23, 2009 - 8am Central time zone) is resolved.
I am still seeing the same issue in Oracle 11.2.0.3. I could not find the bug in support.oracle.com
 
February  25, 2013 - 10:38 am UTC 
 
it is still open (i filed it very low priority).  You may contact support and "take it over" - which can result in a higher priority. 
 
 
Which type of recursive WITH is not supported
Asim, October   03, 2022 - 3:05 pm UTC
 
 
October   04, 2022 - 9:58 am UTC 
 
There are a few possible ways to read that statement:
You can't recursively place WITH clauses inside each other:
with subq1 as (
  with subq2 as (
    select * from dual
  )
    select * from subq2;
)
  select * from subq1;
  
ORA-32034: unsupported use of WITH clauseAs far as I know, no other databases support this.
You can use WITH to write recursive queries, but the RECURSIVE keyword is unsupported in Oracle Database:
with recursive tree as ( ... )
This keyword is part of the SQL standard an other databases do support this.
I agree this could be clearer; I'll follow up with the doc team. 
 
 
Bug 9147631
Asim, October   03, 2022 - 5:38 pm UTC
 
 
The Bug 9147631, initiated in year 2009 is still not resolved.
Its year 2022, and I have checked the UPDATE 
 statement on livesql version 19c
UPDATE scott.emp e
   SET    (e.sal,e.comm) = ( WITH q1 AS (
                 SELECT e2.empno,e2.sal,e2.deptno
                     FROM scott.emp e2),
                      q2 AS (
                           SELECT d.deptno from scott.dept d)
               SELECT q1.sal + 100 sal, 100 comm
                  FROM q1,q2
               where q1.deptno=q2.deptno
                  AND e.empno=q1.empno)
Still the same error
ORA-01767:  UPDATE ... Set expression  must be a subquery
Its strange that Oracles has not fixed this bug till now.
  
October   06, 2022 - 4:23 am UTC 
 
Bugs are prioritised by customer need.
This is why we always tell *customers* to file SRs, and if necessary, reference bug numbers that *we* (Oracle employees) might have logged.
If a customer has never logged an issue with this syntax, it will be prioritised lower (and correctly so) than any customer logged issue. 
 
 
Nested with
Asim, October   04, 2022 - 5:44 pm UTC
 
 
Instead of Recursive WITH, isnt the term "NEsted With" , or "Nesting WITH"  more appropriate for thess types of queries.
with subq1 as (
  with subq2 as (
    select * from dual
  )
    select * from subq2;
)
  select * from subq1;
  
ORA-32034: unsupported use of WITH clause 
October   06, 2022 - 4:20 am UTC 
 
agreed