Skip to Main Content
  • Questions
  • Using temporary tables in stored procedure

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Karl.

Asked: August 13, 2001 - 9:05 pm UTC

Last updated: January 15, 2016 - 2:13 am UTC

Version: v7.3

Viewed 100K+ times! This question is

You Asked

Hi Tom,

I am used to use MS SQL Server or Sybase to create stored procedures for reporting. There, it is very convenient to use temporary tables in the stored procedure to manipulate complex logic. However, in ORACLE, it seems difficult to use. Let say I want to

1) create temp_1 to store the results from "Select col1, col2 from table1, table2 where ..."

2) create temp_2 to store the results from another query

3) Join temp_1 and temp_2 with other tables to get the final results. The results are multiple rows. The application software will call the stored procedure to get the results set.

4) temp_1 and temp_2 should be dropped automatically after running the stored procedure.


How can I accomplish these requirement in a stored procedure? You will be highly appreciated if you could send me a sample stored procedure so that I can learn from it.

Thanks!


Karl Huang

and Tom said...

You should simply:

open ref_cursor for
select *
from ( query you used to put into temp_1 ),
( query you used to put into temp_2 )
where join_conditions

in your procedure.

You'll find that Oracle is much better at complex queries involving dozens (yes more then 16) tables -- without any issues whatsoever.

The temporary table crutch which ismandatory in SQLServer is something you need not use in Oracle -- we are very different -- Oracle and SQLServer are as different as you can get.

See

</code> http://asktom.oracle.com/~tkyte/ResultSets/index.html

for getting result sets from stored procedures.


In this case, I would recommend you totally skip the temporary tables and their unneccesary IO and overhead and go right for the answer.

We support temporary tables, but in a different fashion, see:

http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:48812348054

for a comparision.  DO NOT dynamically create them, DO NOT dynamically create them, please -- do NOT dynamically create them.


<b>followup to comment one below</b>

see
http://asktom.oracle.com/~tkyte/ResultSets/index.html
for examples of ref cursors and their usage.

Anytime you can rewrite in a single query what you are currently doing procedurally or in many steps -- you will be the better for it.

In the example above, they were catering to a database engine that did not perform optimization very well with lots of tables ( very common in that database, that is why temp tables are so overused ).  In Oracle -- even with "real temp tables" in 8i -- this would be the wrong way to approach this problem.

In their case, using a ref cursor is easy, straight forward and cuts out the temp table all together.  It is important to note however that even with the temp table -- they would still need to use a ref cursor (as that is the way to return result sets from stored procedures)

Whether or not you can make use of them will have to be determined by you ultimately.



<b>follow to comment two</b>

Karl -- don't know exactly what you are asking but... here is an example.  Suppose in SQLserver you had coded something like:

begin
   select a,b,c into #t1
     from t1, t2, t3
     where t1.x = t2.y and t2.y = t3.z and t1.c1 = 2;


    select a,d,e,f into #t2
      from t4, t5, t6
     where t4.d = t5.e and t5.e = t6.f and t6.c2 = 55;

    select a.a, a.b, a.c, b.d, b.e, b.f
      from #t1 a, #t2 b
     where a.a = b.a;
end;

In Oracle we would just code:

begin
    open ref_cursor_variable for
    select a.a, a.b, a.c, b.d, b.e, b.f
      from (select a,b,c into #t1
              from t1, t2, t3
              where t1.x = t2.y and t2.y = t3.z and t1.c1 = 2) a, 
           (select a,d,e,f into #t2
              from t4, t5, t6
             where t4.d = t5.e and t5.e = t6.f and t6.c2 = 55) b
      where a.a = b.a;
end;

and that is it -- where you used temp tables, we'll use INLINE views.  Hope that clears it up, if not, feel free to followup.

<b>followup to comment three</b>

No, you are missing the point.

You don't open a REF CURSOR / temporary table from SQLServer -- you use an INLINE view / temp table.  

For example:

scott@ORA8I.WORLD> variable result_set refcursor
scott@ORA8I.WORLD> 
scott@ORA8I.WORLD> begin
  2    open :result_set for
  3          select a.ename, b.dname
  4            from ( select * from emp ) a,
  5                     ( select * from dept ) b
  6           where a.deptno = b.deptno;
  7  end;
  8  /

PL/SQL procedure successfully completed.

scott@ORA8I.WORLD> 
scott@ORA8I.WORLD> print result_set

ENAME      DNAME
---------- --------------
SMITH      RESEARCH
ALLEN      SALES
WARD       SALES
JONES      RESEARCH
MARTIN     SALES
BLAKE      SALES
CLARK      ACCOUNTING
SCOTT      RESEARCH
KING       ACCOUNTING
TURNER     SALES
ADAMS      RESEARCH
JAMES      SALES
FORD       RESEARCH
miller     ACCOUNTING

14 rows selected.

Here "select * from emp" and "select * from dept" represent your "temp table queries" from sqlserver.

If a third table (or fourth or 20th, whatever) needs to be involved -- so be it, just put it in the from clause with the rest of them.


As you can see -- that result_set has >1 row!



<b>followup to comment 4</b>

Karl -- back to my original point here -- we are <b>not</b> sqlserver.

fears like: "I am afraid that the performance would be low because there are many tables opened simultaneously." would be valid there, they are not here in Oracle.

One suggestion -- just try it.  Take a look at my free.sql query:

http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:285415955510 <code>

inline views that are joined and are the results of many table joins themselves (the DBA_* views are horribly complex). Don't be afraid.



follow up to Randy "Always good but... "

I never said you could not use this syntax in SQLSserver -- What I'm saying is that you probably won't use this syntax in SQLServer but you should use it in Oracle.

We have temp tables, you can use them (i have 1 application I developed that uses one). They are useful -- but not as a crutch to help the sql engine digest smaller queries (not in Oracle anyway).

Rating

  (56 ratings)

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

Comments

So are you saying that Ref_cursor is better than temp table

Sonali Kelkar, August 15, 2001 - 9:45 am UTC

In our cases we have real tables as temp (not dynamic which are created inside procedure) and we delete records at the end. We insert values in these tables with complex join queries and then do lot of processing using these inserted values, like insert, update, deletes, some cursors , procedure calls etc. And at the end we delete records for that transaction. But it is a pain maintaining these procedures, I am not also sure if this the fastest way of doing it, performance wise. Tom, do you have a more descriptive example of REF_cursor, I will try to change one of the procedure to use it. We use Oracle 8.0.6 and 8.1.7 but we also support Oracle 7.3 do you think this will work with all these versions ?

Need example using ref_cursor in joining other tables

Karl Huang, August 15, 2001 - 7:29 pm UTC

Hi Tom,

I appreciate your definite answer. What you suggest is to use ref_cursor to store the temporary results. That way we do not need to physicall create 'temporary' permanent tables which are urgly because otherwise when moving the stored procedure from test server to QA to Production, we have to move those 'temporary' tables as well.

Now the question I have is that: after create these ref_cursors, how do they participate in joinning other tables to get the final results set? Are there any good examples showing how the ref_cursor is used in joinning?

Thank again!

Karl

Join ref_cursor1, ref_cursor2 with a table and return a result set

Karl Huang, August 15, 2001 - 8:33 pm UTC

Hi Thomas,

The example you showed me (see "follow to comment two") is assuming that the temporary table has only one row. The temporary table I am using has multiple rows. Let me address my question again:

I need to create temp_table1 and temp_table2, each contains multiple rows. As suggested by Tom, it should use ref_cursor to create temp table. That way, I have ref_cursor1 and ref_cursor2 instead of temp_table1 and temp_table2. Now I need to join ref_cursor1 and ref_cursor2 with a real table, say My_Table and have join conditions to associated ref_cursor1, ref_cursor2, and My_Table. The join will result in a results set, that is, it has multiple rows. How can I do?

Again, an example will be greate help.


Thank you in advance!

Karl


Karl, August 15, 2001 - 9:20 pm UTC

Thanks a lot, Tom, I got your point now. But it is still somewhat different from what I need:

I want to have:

1. cursor_1 for select * from table_1 (join other tables)
cursor_2 for select * from table_2 (join other tables)

2. May need cursor_1 or cursor_2 to join other tables to get
cursor_3

3. Finally I want to have cursor_1, cursor_2, cursor_3 participate in join many tables, say 11 tables, to get the final results set.

If I put everything (including cursor_1, 2, 3,..., and other tables) together to get the final results, it is impossible (usually a report contains many logic which need to be solved indivisually). Even it is possible, I am afraid that the performance would be low because there are many tables opened simultaneously.

Again, please give me an example showing what I need.

Thanks,

Karl




Karl, August 16, 2001 - 11:36 am UTC

Thanks a lot, Tom, I got your point now. But it is still somewhat different from what I need:

I want to have:

1. cursor_1 for select * from table_1 (join other tables)
cursor_2 for select * from table_2 (join other tables)

2. May need cursor_1 or cursor_2 to join other tables to get
cursor_3

3. Finally I want to have cursor_1, cursor_2, cursor_3 participate in join many tables, say 11 tables, to get the final results set.

If I put everything (including cursor_1, 2, 3,..., and other tables) together to get the final results, it is impossible (usually a report contains many logic which need to be solved indivisually). Even it is possible, I am afraid that the performance would be low because there are many tables opened simultaneously.

Again, please give me an example showing what I need.

Thanks,

Karl




Always good but...

Randy, August 16, 2001 - 11:51 am UTC

I'm not sure if the name "inline view" is an Oracle term, but the concept is not particular to Oracle, is it? As long the SQL flavor is ANSI (which SQLServer is), you should be able to do an "inline view". I've been using that syntax for a while in SQL Server AND Oracle, but not too many temp tables in SQL Server (though I have to admit that they are nice). I'm in agreement that I'd rather have Oracle opening many tables simultaneously than SQL Server.

temporary tables

mo, August 30, 2002 - 10:29 pm UTC

TOm:

You wrote this:

In Oracle we would just code:

begin
open ref_cursor_variable for
select a.a, a.b, a.c, b.d, b.e, b.f
from (select a,b,c into #t1
from t1, t2, t3
where t1.x = t2.y and t2.y = t3.z and t1.c1 = 2) a,
(select a,d,e,f into #t2
from t4, t5, t6
where t4.d = t5.e and t5.e = t6.f and t6.c2 = 55) b
where a.a = b.a;
end;

Is this correct oracle syntax "into #t1". what does this do.



Tom Kyte
August 31, 2002 - 10:16 am UTC

No, you just drop the proprietary "into #t1" part and it would be:

begin
open ref_cursor_variable for
select a.a, a.b, a.c, b.d, b.e, b.f
from (select a,b,c
from t1, t2, t3
where t1.x = t2.y and t2.y = t3.z and t1.c1 = 2) a,
(select a,d,e,f
from t4, t5, t6
where t4.d = t5.e and t5.e = t6.f and t6.c2 = 55) b
where a.a = b.a;
end;





temporary tables

Mo, August 31, 2002 - 10:19 am UTC

TOm:

This is what I thought. I never saw this as a valid oracle syntax "into #t1".

Thanks,

temporary tables

Mo, August 31, 2002 - 10:20 am UTC

TOm:

You can also get the same result by johning the set of 5 tables together. Is using inline views better in some ways?

Tom Kyte
August 31, 2002 - 10:44 am UTC

for this trivial example, yes, you could just join the SIX tables together -- the exmaple assumes the queries in the inline views are non-trivial and include things like aggregates perhaps and such which would preclude a simple join.

Your whole site sucks badly

Oracle appears to be an expensive piece of junk, October 12, 2002 - 5:12 pm UTC

1) Stop using persistent cookies, you claim to be in software business.
2) "follow to comment two" Where the hell is "comment two"? Should I count all the comments?



Tom Kyte
October 12, 2002 - 5:34 pm UTC

1) get a grip. have you been to amazon? yahoo? any site of any relevance?

It is OK to block cookies on this site, we just do it to make it easier. Our sessions are maintained via the sessionid in the URL you see (we we track you anyway -- cookie or no). The cookie is only to make it so you don't have to enter you email and other data each time you ask a question, period.

(try disabling cookies on some other sites and see what happens to them sometime)


2) what, they forgot to teach you how to count? That's too bad -- so sorry for you, allow me to try and help you. Comment two would follow right after comment one. comment one is the first comment you see in the reviews section. Got it?


What you are seeing on this page is :

a) we had Q&A's at one point, no follow ups.
b) we decided to let people "review"
c) we discovered that many times we wanted to follow up the review
d) before we added the code to do that, i would follow up in the answer and just "forward reference" it.
e) we added the code so I would just follow up to the review


We did that because people actually seemed to find the Q&A with followups useful.

Sorry you find this not worth your while, hope you don't come back.

Complex queries - without any issues whatsoever?

Sybase user, October 12, 2002 - 6:36 pm UTC

Tom writes: "You'll find that Oracle is much better at complex queries involving dozens (yes more then 16) tables -- without any issues whatsoever."

I would question that it will be “without any issues whatsoever”. AFAIK, the reason why Sybase limits (or used to limit) the number of tables in the join is/was because mathematically it is not that simple to determine the best join order when many tables are involved. Besides, if the table/index statistics is a bit off (and it’s always a bit off when there is activity on the db) optimizer is more likely to make an error when determining the best join order. This optimizer error will be more and more expensive the more tables are involved. Oracle might be better (or not!) than orher dbs when it comes to complex queries but I’m pretty sure that the sequence

BEGIN
SELECT * INTO #temp1 FROM b WHERE ….

UPDATE #temp1 SET … FROM b1 WHERE …
….
UPDATE #temp1 SET … FROM b20 WHERE

SELECT * FROM #temp1
END

will perform BETTER than SELECT that joins 21 tables.

So, if one replaces “SELECT * INTO #temp1” with a cursor, how does one handle those “UPDATE #temp1” in Oracle (providing that he does not want to join 20+ table in one select)?

Thanks

P.S. Author of the ‘comment one’ bring a valid point and it is extremely easy to do this in other dbs.


Tom Kyte
October 14, 2002 - 7:08 am UTC

In Sybase -- you would be correct. Oracle (fortunately) is not sybase.


In Oracle the above sequence would perform more slowly then a single query -- I see it over and over and over.

Do not project their limitation on us.


My apologies, your site is great

A reader, October 13, 2002 - 11:10 pm UTC

I have to apologize for my previous posting. Tom, your site is very helpful and works well with session cookies. I think the issue I had was with OTN, I believe it requires permanent cookies (or maybe it simply it does not work for me). I was trying to get to </code> http://technet.oracle.com/doc/workbench/C5.htm <code>

Anyway, my apologies, thanks for your help.


My apologies, your site is great

A reader, October 13, 2002 - 11:10 pm UTC

I have to apologize for my previous posting. Tom, your site is very helpful and works well with session cookies. I think the issue I had was with OTN, I believe it requires permanent cookies (or maybe it simply it does not work for me). I was trying to get to </code> http://technet.oracle.com/doc/workbench/C5.htm <code>

Anyway, my apologies, thanks for your help.


My apologies, your site is great

A reader, October 13, 2002 - 11:12 pm UTC

I have to apologize for my previous posting. Tom, your site is very helpful and works well with session cookies. I think the issue I had was with OTN, I believe it requires permanent cookies (or maybe it simply it does not work for me). I was trying to get to </code> http://technet.oracle.com/doc/workbench/C5.htm <code>

Anyway, my apologies, thanks for your help.


Complex logic without temp tables

A reader, November 28, 2002 - 2:14 am UTC

Hi Tom,

Very interesting thread indeed. Based on previous discussion it appears that general recommendation is to use cursor as an alternative to (Sybase) temp tables. However, in some cases the logic is so complex that it simply cannot be expressed in a single SELECT. With temp tables handling of such complex cases is easy, one would simply run multiple insert/delete/update on temp table(s) as necessary. How would one handle the same situation in Oracle, without temp tables?

Thanks in advance


Tom Kyte
November 28, 2002 - 7:58 am UTC

give me an example of logic so complex that one is forced -- totally forced -- to use a temporary table vs just working with the real data itself.

Complex logic without temp tables

A reader, November 29, 2002 - 3:20 pm UTC

> give me an example of logic so complex that one is
> forced -- totally forced –
> to use a temporary table vs just working with the real
> data itself.

Hi Tom,

I could definitely give an example where implementing the query logic with one SELECT would be a very difficult task. You’re right though, good SQL developers can be very inventive and, on a couple of occasions, I’ve seen queries about 200 lines long that did the job with one SELECT. In my experience, that kind of queries is difficult to understand (in some cases due to heavy use of characteristic functions) and maintain.

Anyway, my point is that there are situations when implementing the query logic with one SELECT is not possible or practical. How would one handle this in Oracle?

Thanks in advance


Tom Kyte
November 30, 2002 - 8:27 am UTC

INLINE view -- whereever you felt tempted to use a temporary table.

very readable, very understandable, better then procedural code.


I would like to see that example -- of "not possible" but made possible by temporary tables without procedurally processing the results.

Temporary tables in Oracle

Praveen, January 31, 2003 - 9:03 am UTC

Hi Tom,

Tom, I am really messed up with the temporary table concept of Sybase. I started re-writing a big Sybase procedure into Oracle PL/SQL and used a lot of temp tables in the process.
Now I am unable to compile the procedure and was completely stuck until I thought of going through your site again. Now I understand where the problem is but it is now too late to re-write the whole procedures using cursor variables.
I get the error

"identifier 'temp_table' must be declared"

at the line where I tried to insert into the temporary table which I created dynamically. (ie using EXECUTE IMMEDIATE "CREATE GLOBAL TEMPORARY TABLE temp_table....").
The reason is obvious. ie at compile time there is no such object as 'temp_table'.

1) How should I overcome this
problem? (ofcourse with the temporary tables are still created dynamically).

2) Another problem is with the update done on these temporary tables. ie in Sybase they write it as

Ex1: UPDATE temp_table1 t1
SET t1.a1 = t2.a1
FROM temp_table1 t1, another_table2 t2
WHERE t1.pk = t2.pk;

How can I write a similar Update statement in Oracle. ie without using any cursor loops. ?

I saw your answer for a similar question as:

UPDATE (SELECT t1.a FROM temp_table1 t1, another_table2 t2 WHERE t1.pk = t2.pk)

Does this statement gives the same result as the Ex1.

Your valuble suggesion is highly appreciated.

Thanks and regards

Praveen



Tom Kyte
January 31, 2003 - 10:36 am UTC

we do not create temp tables dynamically.

There is no way in the world you even want to consider that. I don't care how far along you are. stop, do it right.


1) by creating the temporary table OUTSIDE of the procedure, ONCE when you install the procedure. Of course temporary tables should NOT be created dynamically.

2) see
</code> http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:273215737113 <code>
for syntax that will work in all databases...

Yes, you update a join update (select....) set ....

Steve, May 01, 2003 - 10:13 am UTC

Hi tom,

Inline view is very convenient because it is easy to query data based on other query's result.

My question is that how can I query the data from the result set generated by a stored procedure as an inline view.

e.g. select col1, col2, ...
from (
a result set from a stored procedure
)
where ....

The stored procedure above is not a single sql, so I can't just copy the sql of stored procedure as inline view.

Thanks!

Steve


Tom Kyte
May 01, 2003 - 12:22 pm UTC

search for

pipelined

on this site.

temporary tables

Steve, May 02, 2003 - 12:00 am UTC

Hi Tom,

Could you please show us how to dump a ref cursor's data (result set)into a temporary table?

Thanks!

Steve

Tom Kyte
May 02, 2003 - 7:57 am UTC

open it
loop
fetch
exit when not found
insert
end
close


that of course would be the wrong approach -- right approach would be:

insert into table select ....;


(eg: the ref cursor is neither necessary nor desired in this case)

Temp Tables / In-Line view

Dilip Dandekar, May 02, 2003 - 5:42 am UTC

After checking at this site I have started using in line views and these are very effective.

Programmer/Analyst

Vishal Sharma, July 03, 2003 - 5:00 pm UTC

I am already using refcursor in procedures to return the results for reporting purposes. It works fine when I run them in sqlplus to verify the results. But the procedures are supposed to support on web.
There are 2 problems I am facing.
1. As the results (a text file) get printed on a network drive, which is read on the web. Procedure completes very fast, but some times it takes 3-4 hours for the file to finish. How can we improve that?
2. Since the procedure is already completed. If i see on the schema, it shows that proc is no more running. While on web (Crystal Report Front Supported) ,it says running because the file is still not over. HOW CAN I see all the refcursors which are still printing ??
We are using {REPORT} user for all those procs.

Thanks in advance.

Tom Kyte
July 03, 2003 - 8:13 pm UTC

1) if the procedure runs fast -- where is the 3-4 hours coming from? missing something here.

2) ahh, now I see. opening a cursor is instantaneous. actually deriving the result set and fetching the result -- that could take a long time.

consider:


open ref_cursor for select * from a_one_BILLION_row_table;

that'll open in a nanosecond.
it might take a while to fetch it all.


use TKPROF and see if you cannot tune your query.

Why should the temp table not created dynamically any specific reason

A reader, July 04, 2003 - 3:00 am UTC


Tom Kyte
July 04, 2003 - 8:33 am UTC

because DDL is expensive, very expensive, to execute.

because you would have to "drop the table" yourself.

because you would have to uniquely name it.

because your stored procedures would have to use dynamic (slower) sql to access it

because all sql accessing this gtt would be hard parsed -- since the object never existed and won't exist for long.

because it is messy and buggy to do so (your code is harder to write, will have more runtime errors, will run slower).

because there is no upside to doing so.

Programmer?Analyst

Vishal Sharma, July 07, 2003 - 12:15 pm UTC

So When The cursor is fetching and printing the results which actually is taking time, Can I query from from database which all refcursors are executing at any perticlar time ???

Tom Kyte
July 07, 2003 - 12:39 pm UTC

v$open_cursor shows you open cursors.

DDL in PL/SQL

Andy, July 23, 2003 - 4:59 am UTC

Tom, I appreciate your point about there being better ways (inline views etc.) to deal with issues that could be dealt with by creating temp. tables from within a stored procedure, but why is DDL not permitted in PL/SLQ in the first place? I came across this quote in the 9i Concepts manual:

"Additionally, you can issue DML or DDL statements using dynamic SQL. This helps solve the problem of not being able to statically embed DDL statements in PL/SQL." (section 14-20)

and was curious as to the reason.

Tom Kyte
July 23, 2003 - 7:59 am UTC


well, it doesn't make sense mostly.

PLSQL is statically compiled. when you compile it -- all references are resolved, permissions checked, etc -- at that time. If you had code like:


create procedure p
as
begin
for x in ( select * from emp ) loop ....
end;


the reference to EMP is "fixed" at compile time. Now, if you tried:

create procedure p
as
begin
create table emp ( x int );
for x in ( select * from emp ) loop ....
drop table emp;
end;


well, obviously at compile time EMP does not exist, so that could never compile.

All you would have achieved had it compiled is some really BAD horrible code (the create/drop commit -- only one person at a time could run this without error since only one EMP table can ever exist -- if you had an error after the create, before the drop, you would have to manually clean up the emp table and so on)




temporary tables

vivek, August 12, 2003 - 10:02 am UTC

Tom,

I am creating temporary tables dynamically in one of mine procedure and then read this thread where you have mentioned

"DO NOT dynamically create them, DO NOT dynamically create
them, please -- do NOT dynamically create them."

Could you please suggest best way to handle this situation.

My client is going to create Work orders and these workorders will be created by their vendors based on 'Contract number' issued to them.

Now vendors are approx. 150 in number and contract number issued per financial year could be in range of 500-600.
What i am doing is creating temporary table based on contract number (which will be unique) and then process the result. Temporary tables are dropped firat and then re-created each time procedure is executed

Now after reading this thread, i am not sure, is this the correct method for this situation.

could you please advise us on this.

thanks

Vivek





Tom Kyte
August 12, 2003 - 10:24 am UTC

permantly create them?



Just CREATE the global temporary table when you install your code. then, use it in your procedures.

done.


temporary tables

vivek, August 12, 2003 - 10:42 am UTC

Thanks Tom.

apologies for posting thrice now..



temporary tables

Roy Garita L., September 17, 2003 - 6:35 pm UTC

Hi... my name is Roy Garita and looking for some information about TEMPORARY TABLES in Oracle. What I need, is to know how they work, for what I should use them, in which cases will be useful and not. How to build them. What are the performances reached among others procedures.

EJM: SELECT a,b,c
FROM (SELECT x,x,y
FROM table1
WHERE ...)
I'll be so happy to find lots of information about it.

Regards.

Tom Kyte
September 17, 2003 - 7:05 pm UTC

use them only when nothing else will work at all...

they work just like tables. except they lose all data when you commit or your session ends -- depending on how they are declared.

you see only your data, no locking/concurrency issues.

inline views are the way to go whenever possible.

i don't really use them very much myself -- except maybe as a big "in list"

Repeated use of some data

Alla, September 17, 2003 - 10:19 pm UTC

Tom;

You say to use temp tables when nothing else works.

My question though is if there is a set of data that I need to use often in a procedure in different statements, would not it make more sense to insert this data into temp table and then use it in all other statements, rather then including this inline view in each and every statement, thus selecting the same data over and over again

Thanks

Why aren't you answering???

Alla, September 22, 2003 - 9:33 am UTC

Hi Tom;

Did this question somehow escaped your attention?

I am still curious what you think about this situation

I would appreciate a response

Thanks

Alla

Tom Kyte
September 22, 2003 - 10:49 am UTC

i don't see each and every review/followup -- no. the UI shows me reviews in chronological order and I try to remember which one i last looked at.

i would only use a temp table if the result set you need is very hard to manufacture (takes a long time to materialize)

Thank you

Alla, September 22, 2003 - 11:27 am UTC


need explanation on temp table usage

aish, October 08, 2003 - 10:03 am UTC


hello,
Here iam trying to insert values to to table child from table master.
Every day this job/procedure is going to run.
This will delete all the contents of child and get a new info from master.
For this purpose iam trying to create some temp table in my pl/sql procedure.
but i have no idea like how to create it .
I dont understand your answer for the abovce question.
How can i use ref cursor in this example.
Thanks
aish

Tom Kyte
October 08, 2003 - 10:57 am UTC

umm, why do you think you need a temp table???

just "insert into select from" -- no temp tables needed, or desired.

problem with quantities

A reader, May 24, 2004 - 10:57 am UTC

We have table T (territory , ship_address ),
T_list (order_id, region, district,territory,quantity).
Where region = substr(territory,1,2),
District=substr(territory,1,4).
After web ordering T_list table can have these data:
ORDER_ID REGION DISTRICT TERRITORY QUANTITY
37 24 10
37 24 2402 5
37 27 2702 2702131 40
37 24 2402 2402131 30
I have to pick up addresses corresponding territories from T and T_list and their quantities from T_list.
The problem is if we have duplicated territories and different quantities.
For example. For Region 24 I have 100 territories and here we will have 10 duplicated addresses because District 2402 has 10 territories. Quantity in case of duplicated territory shoud be next priority:
If territory is specified (not null) it should be quantity with territory (in ex. it's 30), if district is chosen -district qantity (in ex.- 5) and the lowest priority if chosen just region – 10. How can I get rid off duplicated territories and leave right quantity.

Thanks.


Tom Kyte
May 24, 2004 - 11:14 am UTC

please check out the guidelines in big bold text on the followup link....

A reader, May 24, 2004 - 12:24 pm UTC

I have tables T and T_list
Cteate table T (territory varchar2(15), address(varchar2(50);
Create table T_list (order_id number(2),region varchar2(2), district varchar2(4),
Territory varchar2(15), quantity number(5));
Insert into T (‘2402111’, ‘test11’);
Insert into T (‘2402112’, ‘test21’);
Insert into T (‘2402113’, ‘test31’);
Insert into T (‘2402114’, ‘test41’);
Insert into T (‘2402115’, ‘test51’);
Insert into T (‘2402116’, ‘test61’);
Insert into T (‘2405117’, ‘test71’);
Insert into T (‘2402131’, ‘test111’);
Insert into T (‘2702131’, ‘test221’);
Insert into T (‘2802131’, ‘test331’);
Insert into T_list (37,’24’,null,null,10);
Insert into T_list (37,’24’,’2402’,null,5);
Insert into T_list(37,’27’,’2702’,’2702131’,40);
Insert into T_list(37,’24’,’2402’,’2402131’,30);

I need this data:
Territory address quantity
2505117 test71 10
2402111 test11 5
2402112 test21 5
2402113 test31 5
2402115 test51 5
2402116 test61 5
2702131 test331 40
2402131 test111 30

thank you.

Tom Kyte
May 24, 2004 - 1:09 pm UTC

er, you know -- usually I just mostly ignore the stars. but this one, geez.

don't know how I could have made it clearer on the other page. sorry to have bothered you so much for this stuff.

it would have been WAY COOL for it to have actually run too. that is sort of the point here. If you can't be bothered, why should I?

You know, you supplied EXACTLY ONE sql statement that is actually valid SQL.

none of your sql will run. the quotes are not quotes. the inserts are not valid insert statements. the cteate is not a create (and even then there are further syntax errors).....

take a try yourself -- work at it. question is vaguely phrased in the first place. I for one don't even see a way to JOIN T to T_LIST.








A reader, May 24, 2004 - 4:54 pm UTC

I'm sorry for mix up, but I need it.
I have tables T and T_list
Create table T (territory varchar2(15), address varchar2(50));
Create table T_list (order_id number(2),region varchar2(2), district
varchar2(4), Territory varchar2(15), quantity number(5));

Insert into T values (‘2402111’, ‘test11’);
Insert into T values (‘2402112’, ‘test21’);
Insert into T values (‘2402113’, ‘test31’);
Insert into T values (‘2402114’, ‘test41’);
Insert into T values (‘2402115’, ‘test51’);
Insert into T values (‘2402116’, ‘test61’);
Insert into T values (‘2405117’, ‘test71’);
Insert into T values (‘2402131’, ‘test111’);
Insert into T values (‘2702131’, ‘test221’);
Insert into T values (‘2802131’, ‘test331’);
Insert into T values (‘2802221’, ‘test09331’);
Insert into T values (‘2802431’, ‘test87331’);
Insert into T values (‘2802661’, ‘test341’);

Insert into T_list values (37,’24’,null,null,10);
Insert into T_list values (37,’24’,’2402’,null,5);
Insert into T_list values (37,’27’,’2702’,’2702131’,40);
Insert into T_list values (37,’24’,’2402’,’2402131’,30);

Region from T_list is substr(territory,1,2)
District is substr(territory,1,4)

I need : select * from T_list where order_id=37;
Now I have 4 records from T_list.

According to first record I need all territories and addresses with region ‘24’, and quantity 10
let's say
select t.territory, t.address,t_list.quantity from T,T_list
where substr(territory,1,2) = T_list(firstRecord).region
Territory,address,quantity
‘2402111’, ‘test11’,10
‘2402112’, ‘test21’,10
‘2402113’, ‘test31’,10
’2402114’, ‘test41’,10
‘2402115’, ‘test51’,10
‘2402116’, ‘test61’,10
‘2405117’, ‘test71’,10
‘2402131’, ‘test111’10 8 records
According to second record :
select t.territory, t.address,t_list.quantity from T,T_list
where substr(territory,1,4) = T_list(firstRecord).district
‘2402111’, ‘test11’,5
‘2402112’, ‘test21’,5
‘2402113’, ‘test31’,5
’2402114’, ‘test41’,5
‘2402115’, ‘test51’,5
‘2402116’, ‘test61’,5
‘2402131’, ‘test111’5 - 7 records
And for third and forth (just T.territory=T_list.territory)records I have:
‘2702131’, ‘test221’,40,
‘2402131’, ‘test111’,30
Now I have to eliminate duplicated territories and leave quantities with next priority:
If there is territory in T_list take this quantity,
If there district and not territory take this quantity,
And if there is nor district nor territory take region quantity.
The final look:
‘2702131’, ‘test221’,40,
‘2402131’, ‘test111’,30
‘2402111’, ‘test11’,5
‘2402112’, ‘test21’,5
‘2402113’, ‘test31’,5
’2402114’, ‘test41’,5
‘2402115’, ‘test51’,5
‘2402116’, ‘test61’,5
‘2405117’, ‘test71’,10

thank you.



Tom Kyte
May 24, 2004 - 7:06 pm UTC

(your ticks are not ticks -- you are cutting from some MS software perhaps? use "plain text", else I have to change fancy dancy ‘2402115’ into simple '2402115' each time)


the rest of your spec is not clear to me, not following the logic at all (and how would any have known territory/region are very poorly modelled and implemented??? having to substr out fields -- yuck)



not following the "acording to record N" logic as there is no such thing as a first, second or Nth record really - that and I don't see how they records are telling you what to do here.

A reader, May 25, 2004 - 9:06 am UTC

Here is my data:
Table T:
Insert into T values ('2402111', 'test11');
Insert into T values ('2402112', 'test21');
Insert into T values ('2402113', 'test31');
Insert into T values ('2402114', 'test41');
Insert into T values ('2402115', 'test51');
Insert into T values ('2402116', 'test61');
Insert into T values ('2405117', 'test71');
Insert into T values ('2402131', 'test111');
Insert into T values ('2702131', 'test221');
Insert into T values ('2802131', 'test331');
Insert into T values ('2802221', 'test09331');
Insert into T values ('2802431', 'test87331');
Insert into T values ('2802661', 'test341');
Table T_list:
Insert into T_list values (37,'24',null,null,10);
Insert into T_list values (37,'24','2402',null,5);
Insert into T_list values (37,'27','2702','2702131',40);
Insert into T_list values (37,'24','2402','2402131',30);

We get T_list data from web site, the user can chose
region or district, all together, just territory, whatever.
and these data can duplicate each other.
I created procedure where I gather all this data.
than select territory and addresses with needed quantity.
create or replace procedure Ter_distrib_list(Porder in number, paccount_id in number) as
var_address1 varchar2(50);
cursor Cur_f is SELECT territory, qty from ae_temp_distrib_list t1
where qty_flag = 'territory'
or ( qty_flag in ('district', 'region')
and not exists (select null
from ae_temp_distrib_list t2
where t2.territory = t1.territory
and t2.qty_flag in ('territory', decode(t1.qty_flag, 'region', 'district'))))
order by territory;
begin
Delete from ae_temp_distrib_list;
commit;
for X in ( select region,district,territory, quantity from T_list where order_id=Porder ) loop
if X.territory is not null then
select address into var_address1 from T
where territory=X.territory;
if var_address1 is not null then
insert into ae_temp_distrib_list (territory, address1, qty, qty_flag)
values (x.territory, var_address1, x.quantity,'territory');
end if;
dbms_output.put_line('territory - '||X.territory);
elsif X.district is not null then
for D in (select territory, address from T where substr(territory,1,4) = X.district) loop
insert into ae_temp_distrib_list (territory, address1, qty, qty_flag)
values (D.territory,D.address,X.quantity,'district');
end loop;
elsif X.region is not null then
for R in (select territory, address from T where substr(territory,1,2) = X.region ) loop
insert into ae_temp_distrib_list (territory, address1, qty, qty_flag)
values (R.territory, R.address,X.quantity,'region');
end loop;
end if;
end loop;
commit;
for var_final in cur_f loop
dbms_output.put_line(var_final.territory || ' ' || var_final.qty);
end loop;
end;
can I do the same without temporary table and store procedure?



Tom Kyte
May 25, 2004 - 11:01 am UTC

could you just say it in english? spec like? reverse engineering code takes *forever* and I would have to live with what the code does as written, not with what it could/should do.

(but in reality, if you have a working, debugged routine that runs faster then fast enough..... well....)

A reader, July 01, 2004 - 11:12 am UTC

Tom ,
I have a personal question.
How are you able to become an authority on Oracle. I want to become some one like you

Tom Kyte
July 01, 2004 - 11:37 am UTC

16 years of doing it day after day after day.

"experience"

get on the groups at google, comp.databases.oracle.*

start answering questions, researching them, defending your answers with proofs. I started in 1994 - thats when I really got going.

Pravesh Karthik from Indai, August 24, 2004 - 9:42 am UTC

Tom,

i have many tables in my schema. how to take the procedures that are associated with those tables, i did exp of those tables, how to exp the procedures ..


Please help me

Thanks

Tom Kyte
August 24, 2004 - 10:22 am UTC

search this site for

getcode getallcode

Pravesh Karthik from India, August 24, 2004 - 9:43 am UTC


--- i mean there are many tables, only selected set of tables i have exp'ed i need those proc that uses only selected set of tables ... possible


Thanks ...

Tom Kyte
August 24, 2004 - 10:26 am UTC

nope. no such tool.. you could write a query on dba_dependencies (a recursive one) to find the list if you like.

Temp Tables, Ref Cursors and......

Rob H, October 26, 2004 - 12:54 pm UTC

I have been given a simple problem that I was initially going to use temp tables for, but now that read this I think I may need ref cursors.

I have a table
Printed_cheques(
Cheque_id number,
Customer_id number,
cheque_amount number,
cheque_order number)

The problem is during a "print run" we may end up destroying a physical cheque. So, I will need to look thru the table, update the cheque to the next (or new cheque Number) and then re-number all the cheques that follow in sequence. The sequence must be in cheque_order.

My inital plan was to create a temp table, move the data in, truncate the old table, drop a trigger on cheque_id and recreate it with the new cheque number and insert from the temp table into the print_cheque table.

This seems like a lot of overhead. Should I put the data in a ref cursor/truncate/insert? Or would it be better to go thru each row and update the cheque id? Is there a better way than a trigger?

I apologize, I'm a new DBA and very limited developer. However, I am trying to learn. This seems like a very simple problem.

Tom Kyte
October 26, 2004 - 1:03 pm UTC

can you give me before/after data showing the logic?

are you really updating "row 5" and giving it the cheque_id from "row 6" and updating the last row to have a brand new "cheque_id" (and if so, where does that come from?)

Example Data

Rob H, October 26, 2004 - 1:42 pm UTC

Cheque_id, customer_id, cheque_amount, cheque_order
108,6,660.09,1
109,15,45.01,2
110,3,202.10,3
111,7,116.45,4
112,19,11.77,5

Now during the printing of the cheques a failure occurs at
110,3,202.10,3 <-- Printed Fine
111,7,116.45,4 <--- Printer Error

Physical cheque numbered 111 has to be discarded. I must reset cheque number 111 to 112 and cheque 112 to 113, etc. Also, a similar problem may be that we're starting a new cheque batch, ie cheque 110 needs to be changed to cheque number 250, and thus cheque 111 to 251, cheque 112 to 252, etc.

The cheques must be numbered in Cheque_order. I understand that the field cheque order may not actually be required.

Currently, I have a manual process of creating a temp table, moving the data that needs to be re-numbered in, dropping the sequence on cheque_id, recreating the sequence with new start cheque number, removing the data that needs to be re-numbered from the print_cheque table, moving the data from the temp table back using the new sequence.

This seems like a ton of overhead. I know there is a simpler way. I am wondering if ref cursors are the way to go? Should I even use a sequence? What about just re-numbering in the table itself (I have no idea how to do that.) I am new at PL/SQL, do I need to do PL/SQL?
Should i use an update?

Thanks for the quick response.

Tom Kyte
October 26, 2004 - 2:40 pm UTC

is cheque_order assured to be a number that increases one by one or not?

In Theory

Rob H, October 26, 2004 - 2:42 pm UTC

Yes, in theory, cheque_order is sequential. However, I don't like theory. I need something that will work long term. It is entirely possible that a customer may have to be removed and the cheques re-numbered from the customers cheque number onward.

Tom Kyte
October 26, 2004 - 3:00 pm UTC

ok (i'm just feeling my way around your data model here, just trying to understand what I've got to work with). could do with a simple update if check_order is sequential (sorry - i've got to use ck -- que just isn't coming out of my finger tips :)

<b>so, lets start with this</b>

ops$tkyte@ORA9IR2> select * from t;
 
  CHECK_ID    CUST_ID  CHECK_AMT CHECK_ORDER
---------- ---------- ---------- -----------
       108          6     660.09           1
       109         15      45.01           2
       110          3      202.1           3
       111          7     116.45           4
       112         19      11.77           5
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> variable bad_check_id number
ops$tkyte@ORA9IR2> exec :bad_check_id := 111
 
PL/SQL procedure successfully completed.

<b>fix checks starting at check id 111:</b>
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> merge into t
  2  using ( select rowid rid,
  3                 nvl(lead(check_id) over (order by check_order),check_id+1) new_check_id
  4            from t
  5           where check_order >= (select check_order
  6                                   from t
  7                                  where check_id = :bad_check_id)) x
  8  on (t.rowid = x.rid)
  9  when matched then update set check_id = new_check_id
 10  when not matched then insert (check_id)values(null);
 
2 rows merged.

<b>it is impossible for the when not matched to happen (in 10g we can just leave it off in fact)</b>

 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> select * from t;
 
  CHECK_ID    CUST_ID  CHECK_AMT CHECK_ORDER
---------- ---------- ---------- -----------
       108          6     660.09           1
       109         15      45.01           2
       110          3      202.1           3
       112          7     116.45           4
       113         19      11.77           5

<b>Now we want to make 112, 113, ... = 250, 251, ....</b>

 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> variable new_check_id number
ops$tkyte@ORA9IR2> exec :bad_check_id := 112; :new_check_id := 250;
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> merge into t
  2  using ( select rowid rid,
  3                 :new_check_id+row_number() over (order by check_order)-1 new_check_id
  4            from t
  5           where check_order >= (select check_order
  6                                   from t
  7                                  where check_id = :bad_check_id)) x
  8  on (t.rowid = x.rid)
  9  when matched then update set check_id = new_check_id
 10  when not matched then insert (check_id)values(null);
 
2 rows merged.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> select * from t;
 
  CHECK_ID    CUST_ID  CHECK_AMT CHECK_ORDER
---------- ---------- ---------- -----------
       108          6     660.09           1
       109         15      45.01           2
       110          3      202.1           3
       250          7     116.45           4
       251         19      11.77           5
 
ops$tkyte@ORA9IR2>



 

Excellent

Rob H, October 26, 2004 - 4:22 pm UTC

That is excellent, passed all the tests I threw at it. I hate it when you do that. Makes me feel like such an idiot....so simple.

I was thinking of using a procedure like:

Create or Replace Procedure renumber_cheque
(bad_cheque_id number, new_cheque_id number) authid current_user IS
begin
merge into printed_cheques t using
(select rowid rid, new_cheque_id+row_number() over (order by cheque_order-1) new_cheque_id
from printed_cheques
where cheque_order >=
(select cheque_order from printed_cheques where cheque_id = bad_cheque_id)
) s
on (t.rowid = s.rid)
when matched then update set cheque_id = new_cheque_id
when not matched then insert (cheque_id) values(null);
commit;
end;

but for some reason this

Begin
RENUMBER_CHEQUE
(
1,
1027
);
End;

Will start the cheques with 1028, not 1027 (as an example).

Tom Kyte
October 26, 2004 - 5:32 pm UTC

new_cheque_id+row_number() over (order by cheque_order-1) new_cheque_id

vs

:new_check_id+row_number() over (order by check_order)-1 new_check_id


note the subtly different placement of -1 :)


A change

Rob H, October 26, 2004 - 4:28 pm UTC

I changed the line
when matched then update set cheque_id = new_cheque_id
to
when matched then update set cheque_id = new_cheque_id-1

and the procedure works as expected. Thanks.

Any idea why in SQL Plus it works exactly, but in the procedure its different?

Tom Kyte
October 26, 2004 - 5:37 pm UTC

see above

Doh

Rob H, October 26, 2004 - 6:01 pm UTC

Yes, I put the additional comment too quickly. Once again, you have proven your "worthyness".


Cursors with temp tables...

Eray BALAT, June 29, 2005 - 8:46 am UTC


Hi Tom;
I have read all about the cursors and temp tables.And I still have one question, that is a little bit ridiculous but i have to ask.
I want to copy some rows from my main table to my temp table.I know that I can do it my
"insert into _ select * from" but can I do same thing with cursors using cursors

One step further.I must have 2 cursor.When i select from one cursor can i send this row's id to the second one as a reference to select??

Thanks
Eray BALAT

Tom Kyte
June 29, 2005 - 9:21 am UTC

sure, you can use slow by slow processing via a cursor or you can do it right with an insert as select.

a global temporary table, a real table, it doesn't matter, you can use a cursor to whip through any result set.


that last paragraph scares me, yes you can - -but boy, would it be wrong. databases where BORN TO JOIN, it is what they do best.

A reader, January 04, 2007 - 4:56 pm UTC


temporary tables again...

pablo, December 10, 2008 - 4:13 am UTC

You wrote:
"give me an example of logic so complex that one is forced -- totally forced -- to use a temporary
table vs just working with the real data itself. "
I have a case that I think I'll have to use temporary tables.
My main query goes through bank transactions (imported from bank statements) given a bank account and a date interval.
For each one of them, I try to find a match using bank account id, currency date and amount in a view with quite a complex logic (against many tables in GL, AP and AR). This select takes about 5 seconds for every bank transaction.
If I insert the entire date interval from my view into a temporary table and search in it instead, the performance is a thousand times better.
Do you see any other way to avoid the temporary table or is this what they are created for (because there is a reason why they exist I guess).
Thanks and regards.


Tom Kyte
December 10, 2008 - 9:51 am UTC

with data
as
(your query you are thinking about putting into a temporary table)
select *
from data
where condition;


Performance related to temp table

Samy, March 23, 2009 - 6:24 am UTC

Hi Tom,

i just wanted to know your views, i have a table Table_Org with Lakhs of records, i have create a table Table_Tmp similar to Table_Org. In Procedure Based on Parameter inserts record from Table_Org into Table_Tmp which might be 10 to 20 records and do Process on the Table_Tmp and at the end of the Procedure i just Update Table_Org with value of Table_Tmp.


let me know Use of temp table will it help to Speed up my Procedure

Tom Kyte
March 26, 2009 - 12:37 pm UTC

it'll probably make it slower, why do you think it would make it faster?

Before you do something, you should have a good idea of what will happen because you are doing that thing. Therefore, you MUST have some technical thought in your head to complete this sentence:

I am using a global temporary table because ............

Where ......... is not "i think it will be faster" but rather a technical reason, some compelling thought, something concrete that we could look at and say "yes, using a temporary table might make sense there"


(this next example is NOT your example, but rather a sound argument for using a temporary table)

I am using a global temporary table because I have a complex subquery that generates an in list, I need to use this in list in 10 'insert as select' statements. The query to generate this in list of items takes a long time (10 seconds) to execute, if I take the couple hundred rows it generates and plop them into a temporary table, retrieval of them takes 0.1 second instead. So it should shave about 90 seconds of runtime off of my process (I run it once for 10 seconds instead of 10 times for 10 seconds).


So, what is your ........... reasoning?

Update Question

Wade, October 21, 2009 - 3:04 pm UTC

I noticed that the original question was asked in 2001 and I am wondering if the same response would be given if asked today. Until recently I worked exclusively in MSSQL and am now coding on Oracle, so these types of issues will hopefully help me to do a better job in my new world. Thanks.
Tom Kyte
October 23, 2009 - 12:49 pm UTC

the answer remains the same.

global temporary tables are hardly ever needed or desired in Oracle.

How to re-use query results without temporary table

Antonio Lopes, March 28, 2012 - 6:13 am UTC

Hi Tom,

Thank you for this detailed explanation about the temporary table issues.

I still have one doubt however after reading this.

I have a relatively heavy query for which I want to use the results in another queries. In the end, i want to combine the results (union) in a final query (cursor) to return to the user.

Take this on SQLServer:

-- heavy query with several joins
select id, c1, c2 from t1,... into #t1

select * from
(
select c1, c2, c3, 1 from t2 where t2.c4 = #t1.id
union
select c1, c2, c5, 2 from t3 where t3.c6 = #t1.id
)

I am probably missing the point here with inline views but how can I rewrite this logic in Oracle, given that I do not want to have the heavy query be executed/computed several times for the joins with the other tables?

Tom Kyte
March 28, 2012 - 9:24 am UTC

I don't see how you are legally using t1 in the union - I don't see any references to t1 in the from list.

But assuming that was just accidentally left out:


with data as (select id, c1, c2 from t1, ..... )
select * from 
( select c1, c2, c3, 1 from t2, data where t2.c4 = data.id
  union
  select c1, c2, c5, 2 from t3, data where t3.c6 = data.id
)

WITH clause

Antonio Lopes, April 03, 2012 - 6:28 am UTC

Tom, you're a life savior!

I was not aware of the WITH clause in Oracle... it seems it is SQL-99 and named "query factoring". It does the trick that I needed!

Yes I am using T1 table (and others) by referring to temporary table #t1 in the other queries in the UNION. The WITH clause keeps the "reference" to the heavy subquery and lets me use it in other joins without having to recompute it again. And no need to use temporary tables (nevertheless Oracle may be using one behind the scenes for the WITH clause?)!

Tom Kyte
April 03, 2012 - 6:51 am UTC

correct.

when to use temporary table?

A reader, April 04, 2012 - 10:29 pm UTC

Hi Tom,

Just would like to have your opinion on the usage of temporary tables.

Per your response in this thread, it seems temporary table has very less using scenarios.

According to my understanding, we use temporary table as below, any other cases?
1. to solve mutating table error, before insert or update create a temporary table copy, which make
trigger is reading from one table and modifing another different table
2. to hold data from some dynamic views (V$) when we need to join with them, as they are sometimes very huge and changing all the time
Tom Kyte
April 06, 2012 - 9:47 am UTC

1) if you have a mutating table issue - you probably have other issues you are not even aware of. I would strongly encourage the avoidance of triggers in that case

http://www.oracle.com/technetwork/issue-archive/2008/08-sep/o58asktom-101055.html

so no, I would not recommend them here.

2) maybe, but most of what you need should probably already exist in AWR/ASH tables - or gained by using statspack.

Multiple Operations

Tim, January 12, 2016 - 8:39 pm UTC

Not sure this is the best place to ask this, but I stumbled on this thread while thinking about using a Temporary Table to solve my problem, so I will ask here what the right way to do this is.

I have an analytic database that is aggregating a couple of tables together for our customer base. It has moving time frames (like last 6 months, last 2 years, etc) with counts and averages which need to be refreshed as transactions move in and out of buckets. I have the logic to calculate when records need to be refreshed, I am just trying to determine the best way to actually do the refresh.

I started originally with a slow-by-slow insert/update block. The initial tests looked like the updates should take about an hour, but they started at 8 hours and grew to past 24, so I shut it down and I am looking to refactor the refresh into a single statement (or a pair of single statements).

My initial thought was to get a list of all of the customers who's data needs to be refreshed and delete the existing records in one big delete statement, then insert with one big insert statement. But, the search clause to get the data is fairly expensive and requires the data I am going to delete to get the correct results, so I was thinking of stashing the results of that query in a temporary table instead. Is there a better way? I did some research into the WITH clause you proposed for another solution, but I didn't find anything that suggested it could be used with both an INSERT and a DELETE clause. I have used it on occasion in the past and didn't think it would work for me, but I looked anyway.

I can provide more specific specs for what I am doing if you need them, but I don't need a specific solution provided, just a pointer to the solution you think would work best for me.
Connor McDonald
January 13, 2016 - 1:34 am UTC

Without specifics, the basic principle is always deletes are expensive, updates are expensive, inserts are cheap

So if I need to update/delete lots of rows, I'll explore switching to loading the data I want to keep somewhere else.

That might be a table level

- load a temporary one, truncate the real one, and load the temporary data in

or

- load a new table, rename it to become the real one


Or it might be partition level (truncate/drop/load/exchange)



More info

A reader, January 13, 2016 - 6:17 pm UTC

The current table in prod has about 400 million rows. I anticipate about 500 thousand rows to be changed daily, and it is possible that we could need to change several days' worth of data if the refresh process fails. I am thinking the upper limit is probably in the 2-3 million range. There are 92 columns in the table, and the average row length is 232. The table is currently at 105 GB.

For partitioning, we are a hotel franchise company and we own several different brands. This table is keyed on customer_id and brand_id. Most customers have a single brand. We could partition based on the NEXT_REFRESH date that I calculate based on the moving buckets, but if the customer stays at a hotel tomorrow, we would need to refresh that row even if the NEXT_REFRESH date is in 6 months, so that is a difficult key to partition off of as well.

I do have other aggregate tables where I rebuild it daily (some of them are full rebuilds and some of them are piecewise like you suggest here). I actually have two identical tables _A and _B with a synonym that swaps between them, so that would not be a big stretch to do here. If the data is in _A, I load all of the data into _B, switch the synonym, then truncate _A, and then do the reverse tomorrow. I can do that here, too, but we are likely only modifying less than a percent of the table, so I am not sure the cost/benefit is in favor of a full load.

Do you still think that writing to a new table is the best solution for me to try? Is there helpful information for this that I have omitted? If I should go the route of DELETE/INSERT, is there a good way for me to not have to run the query of who needs to be reloaded twice?
Chris Saxon
January 14, 2016 - 1:46 am UTC

2-3 millions could be "not much" or it could be "a lot", depending on things like the number of indexes you have, constraints in place, potentially even triggers and so forth.

Similarly, the number of columns altered (and whether they are indexed) becomes a decision point between update vs del/ins.

But your inference that set-based SQL operations will be better than row-by-row is spot on, and obviously opens up possibilities for parallel processing and/or direct path load if applicable.

I wouldnt discount partitioning out of hand. Even if its not a natural fit, sometimes a hybrid solution presents itself. For example, I once had a similar "refresh all changed data" requirement at a client, where with some data analysis we saw that *most* changes occurred to data that was created less than 1 week before the current date. Other data created well before that was also changed, but it was a small percentage.

So we did partitioning by week, and refreshed the current week using partition exchange to efficiently process (say) 90% of the data, and then used standard del/ins to pick up the remaining 10%.


Good options

A reader, January 14, 2016 - 6:02 pm UTC

Thank you, that partitioning idea is really cool. I am trying to figure out of we can find some partition key for this table to use that to our advantage. So far, I haven't been able to, but that idea might actually be good for other pre-calculated tables in our system, so I might use that for some future development.


I am going to try a few different solutions and try them side-by-side. I do want to try the delete/insert option while I am testing these things. So, I still have one more question. I have a query that returns a list of IDs. It is fairly expensive, so I would prefer not to run it more than once. It also looks at data in the table that will get deleted and reinserted into, so if I run the delete, I will lose all of the records that it is telling me need to be inserted.

What is the best way to get a single result set and feed it into multiple operations? The only way I know of is to use a global temporary table (or an actual table, but then I need to do extra cleanup and there is other overhead associated with that). I was thinking of creating a single table called like TMP_ID_LOOKUP and make it generic so I can use it in the future if I need to. One NUMBER column that I can just insert into with my select, then join in both my delete and insert statements.

Temp tables are the only way I know of to really solve for this, but after reading this thread, I want to avoid them if there is a better solution for this.
Chris Saxon
January 15, 2016 - 2:13 am UTC

There is nothing wrong with temporary tables...the original premise of this question was in regard to SQL Server, where temp tables are used very frequently for consistent read.

For your purposes, it could be a good option. They are very cheap to populate (especially in 12c), and there is no real "maintenance" if they are "on commit delete rows", because you

- load the temp table
- load your data
- commit

and voila, the temp table is empty and ready to go again

Thank you.

Tim, January 15, 2016 - 6:52 pm UTC

Sounds good. Thank you for the clarification. I read through a bunch of the postings again yesterday after posting and I was getting the impression that you were talking strictly in the context of using a Temp Table where an in-line view would work, but I wasn't sure. It is also good to get some info on the temp table use in TSQL. I was asked to help out on some SSRS stuff at this job a couple of years ago, and I was baffled by the amount of temp tables used in the stored procedures. Now I understand why they used so many.

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