Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Anne.

Asked: November 16, 2001 - 6:21 pm UTC

Last updated: December 08, 2005 - 1:31 am UTC

Version: 8i

Viewed 1000+ times

You Asked

From an earlier Question/Answer you write ...

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Question:
5.Will there be any performance improvement using global temporary tables? can you give an example where using a global temporary tables improves performance?

Answer:
5) depends on what you are doing. I don't use them or "intermediate" result sets myself as many people are brain washed into doing from experiences with SQLServer and the like. I do the work in a single query. .....
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

This intriques me. I have a procedure that needs to return a cursor which, if done in one pass, looks to me like it will be hideously long. The concept of using an "intermediate" result and updating that seems like the only way to ensure that it doesn't execute parts more times than necessary. My simplified example:

Transaction Table:
part_no varchar2(10)
qty_sold number(8)
tran_date date
price number(8,2)
* table could have 200,000 rows; 25 unique part_no's

I need to return a cursor in a stored procedure that contains one row for each unique part_no (ie 25 rows) and contains
1. part_no
2. Total qty_sold
3. Last price that the part was sold for

I can't think of how to do that in one query, much less ensure that the "last price" isn't searched for 200,000 times. Can you tell me the BEST way to do this, considering that we are dealing with a Data Warehouse on the backend?

Thanks


and Tom said...

Here is a trick i use when I need to get data like "the last price that the part was sold for". Assume I have data loaded up (your 200,000 rows)

DOC>create table t ( part_no varchar2(10),
DOC> qty_sold number,
DOC> tran_date date,
DOC> price number );
DOC>declare
DOC> l_part_no varchar2(10);
DOC>begin
DOC> for i in 1 .. 25
DOC> loop
DOC> l_part_no := dbms_random.string( 'a', 10 );
DOC> for i in 1 .. 8000
DOC> loop
DOC> insert into t values ( l_part_no,
DOC> dbms_random.value( 1, 1000 ),
DOC> sysdate-dbms_random.value(1,1000),
DOC> dbms_random.value( 1, 1000 )/2 );
DOC> end loop;
DOC> end loop;
DOC>end loop;
DOC>/
DOC>
DOC>
DOC>*/

I run a query like this:

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select part_no, qty, to_number(substr(max_price,16)) price
2 from (
3 select part_no,
4 sum(qty_sold) qty,
5 max( to_char( tran_date, 'yyyymmddhh24miss') || '/' || price )
max_price
6 from t
7 group by part_no
8 )
9 /

PART_NO QTY PRICE
---------- ---------- ----------
BH\rgPdOAm 4015218.21 317.848247
DMtESN`BZF 3970748.25 280.408863
Ebo`gDiSFF 3954301.05 480.440545
Efro\CGFP\ 3989586.15 110.796655
FbDPCVV[U\ 3967964.1 151.394533
Lm\Mkq[``Z 4006725.73 318.423974
MQdAFaWPIo 4023327.35 461.226982
U[i[NJRtd] 4009448.18 278.874234
UtNAQfemYP 3981840.55 376.057365
V[fLTRoElp 4006070.01 282.55744
VrBkkS``ii 3990596.6 444.806519
XH\sFTDKm] 3997581.06 264.543268
YhC[acePi] 3983697.35 108.051863
[OpPOBTbj_ 4016994.35 401.426913
_VTgLDjhht 3994016.41 357.018348
d[oeF_jngA 3996627.73 198.383359
eKDCh_RrqO 3954680.99 174.840716
i`NHiYRaKc 4027057.86 213.274092
kaNZgBcREr 4043014.26 219.62832
kjLm`kTIB_ 3998182.66 365.428061
l`LFbDU_MV 3999645.75 73.9165855
nLRRT`PWaV 4005948.4 386.140121
oILFqpKJKV 3991702.4 250.929279
qOZloLfQCN 4025751.88 359.500798
rolRAmEstl 4013841.69 288.579791

25 rows selected.

What I did was glue the TRAN_DATE and PRICE together into a string that sorted properly -- take the MAX (gives the max tran_date) and substr back out the price.

Makes one pass at the table, on my system, tkprof says:



select part_no, qty, to_number(substr(max_price,16)) price
from (
select part_no,
sum(qty_sold) qty,
max( to_char( tran_date, 'yyyymmddhh24miss') || '/' || price ) max_price
from t
group by part_no
)

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.01 0.01 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 3 4.89 4.88 0 1870 6 25
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 5 4.90 4.89 0 1870 6 25

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 63

Rows Row Source Operation
------- ---------------------------------------------------
25 VIEW
25 SORT GROUP BY
200000 TABLE ACCESS FULL T

little less then 5 seconds on my machine -- and no double reads of any data....

Rating

  (6 ratings)

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

Comments

Bravo

Mark, November 16, 2001 - 7:52 pm UTC

short, sweet and elegant. very helpful.

Temporary table vs single query

Anne C Murphy, November 19, 2001 - 12:20 pm UTC

This is exactly what I needed and saves hours of work, and wasted time from going down the wrong path.
Thank you so much.

absolute

bala, March 30, 2002 - 12:03 am UTC

crisp,great.........

when to use temp table

Steve, April 23, 2004 - 10:21 am UTC

Hi Tom

In the following case,

update T1
set c1= v1,
c2= v2
where (pk1, pk2) in ( select pk1,pk2
from
( select some cols from T3,
some cols from T4,
some cols from T5,
...
from T3,T4,T5,T6
where t3.c1 = t4.c1
and t3.c2 = t5.c2
and other join condition
)
)

update T2
set c = ( select col1
from (
select some cols from T3,
some cols from T4,
some cols from T5,
...
from T3,T4,T5,T6
where t3.c1 = t4.c1
and t3.c2 = t5.c2
and other join condition

)
)

Should I create temp table and

insert into temp_table
select some cols from T3,
some cols from T4,
some cols from T5
....
from T3,T4,T5,T6
where t3.c1 = t4.c1
and t3.c2 = t5.c2
and other join conditions ......

and then replace the query with the temp table in above update statements. If the query is big, should I use temp table instead of single query from the performance wise and maintainable wise?


Thanks!

Steve


Tom Kyte
April 23, 2004 - 1:44 pm UTC

hard to say -- as a literal reading of the example would have me believe that the query that is repeated returns but a single row -- so it probably goes really fast.


Sure, if the query takes "a really long time (tm)" to execute, resolving it into a temporary table could have some benefit -- it would have to take some significant amount of time however to offset the extra work you are doing.

Otherwise, no.

temp table to single query

Steve, December 06, 2005 - 5:04 pm UTC


Hi Tom,

For the following temp table usage pattern,
I converted the statements 1,2,3 into 4,
the statement 4 is not efficient. What do you think if I change them into 2 update statements by removing union operator?



1. trunc table work

2.
insert into work (id, new_value)
(
select s.id, b.new_value
from S, B
where s.id = b.id
union
select s.id, c.new_value
from S, C
where s.id = c.id
)

3.
update
(
select s.id,
s.old_value s_old_value,
w.new_value w_new_value
from S, work w
where s.id = w.id
)
set s.old_value = w.new_value

4.
update
(
select s.id,
s.old_value s_old_value,
w.new_value w_new_value
from S,
(select s.id, b.new_value
from S, B
where s.id = b.id
Union
select s.id, c.new_value
from S, C
where s.id = c.id
) W
where s.id = w.id
)
set s.old_value = w.new_value

Thanks!






Tom Kyte
December 07, 2005 - 1:37 am UTC

are you sure you meant union or did you mean union all.


statement 4 is not "inefficient", it is not executable.  What statement am I really comparing to here?

ops$tkyte@ORA10GR2> l
  1     update
  2      (
  3         select s.id,
  4                s.old_value s_old_value,
  5                w.new_value w_new_value
  6         from S,
  7                (select s.id, b.new_value
  8                   from S, B
  9                where s.id = b.id
 10                Union
 11                select s.id, c.new_value
 12                from S, C
 13                 where s.id = c.id
 14                ) W
 15         where s.id = w.id
 16      )
 17*     set s_old_value = w_new_value
ops$tkyte@ORA10GR2> /
    set s_old_value = w_new_value
        *
ERROR at line 17:
ORA-01779: cannot modify a column which maps to a non key-preserved table
 

continue my followup

Steve, December 07, 2005 - 12:15 pm UTC


Sorry Tom!
So I can't convert statment 1,2, and 3
into a single sql. what I can do is to replace
above 1,2,3 into 2 updates
update (
select s.id,
s.old_value s_old_value,
b.new_value b_new_value
from S, B
where s.id = b.id
)
set s_old_value = b_new_value

update (
select s.id,
s.old_value s_old_value,
c.new_value c_new_value
from S, C
where s.id = c.id
)
set s_old_value = c_new_value

I am wondering if you can convert this temp table
usage oattern into single query. If not, what way do you think better? temp table way and 2 Update statements way.

Thanks!




Tom Kyte
December 08, 2005 - 1:31 am UTC

In 10g, with merge and just an update clause - yes, else not really


you better make darn sure B and C never have the same ID - seems B and C should be just one table in the first place.

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library