How to create a temporary table inside Stored Procedure before opening a cursor?

元气小坏坏 提交于 2020-05-29 11:38:51

问题


I'm trying to create a temporary table within a Stored Procedure to perform multiple operations on it and then execute a select statement. I haven't had success using global temporary tables. When I execute the code below I get an ORA-00942 error (table or view does not exists). I must return the cursor to use it in Spring. Any ideas?

PROCEDURE DELETME (
          O_CURSOR         OUT tCursor,
          COD_ERROR        OUT NUMBER,
          MSM_ERROR        OUT VARCHAR2
          )
     AS
BEGIN
    execute immediate 'create global temporary table my_temp_table(column1 number) on commit delete rows';
     insert into my_temp_table(column1) values (1);
    COD_ERROR := 1;
    OPEN o_cursor FOR
           select * from my_temp_table;
EXCEPTION 
    WHEN OTHERS THEN
              COD_ERROR:=0;
              MSM_ERROR:=dbms_utility.format_error_backtrace ||' '||SQLERRM;
END DELETME;

回答1:


Your procedure won't compile because you have dependencies on TEMP_TABLE which does not exist. Hence the ora-00942. Of course, if it does exist then your procedure will fail at runtime: the create global temporary table call will fail, because the table already exists.

Basically you have misunderstood the purpose of global temporary tables in Oracle. They are permanent structures, it is just the data they hold which is transient. This is a common issue, especially for people who are familiar with SQL Server and are trying to convert T-SQL into Oracle. Temporary tables in MSSQL are more like PL/SQL collections.

Obviously your posted code is a toy so it's not clear why you think you need a temporary table. It's quite likely that you don't, as Oracle SQL is pretty powerful. Chances are you can just open a ref cursor for a complex SELECT statement.

But it you do happen to have some real need this is how to do it:

First, as a one-off exercise:

create global temporary table my_temp_table
   (column1 number) on commit delete rows   
    tablespace temporary_ts;

Note the tablespace clause: GTTs write to disk not memory, which means they are slow to populate and slow to read. If you're going to use GTTs it's a good idea to have a dedicated temporary tablespace just for them, because they have a different usage profile compare to other temporary processes such as sorts.

Anyway, your procedure becomes

PROCEDURE DELETME (
          O_CURSOR         OUT tCursor,
          COD_ERROR        OUT NUMBER,
          MSM_ERROR        OUT VARCHAR2
          )
     AS
BEGIN
    insert into my_temp_table(column1) values (1);
    COD_ERROR := 1;
    OPEN o_cursor FOR
           select * from my_temp_table;
EXCEPTION 
    WHEN OTHERS THEN
              COD_ERROR:=0;
              MSM_ERROR:=dbms_utility.format_error_backtrace ||' '||SQLERRM;
END DELETME;

Remember you need to issue a commit (or rollback) to clear your table; if you don't housekeep it that may create problems when the same session re-uses it.

Alternatively, use a collection instead. Collections are much faster, because they are memory structures. Although the memory comes from session allocations so this is not the best solution if the total number of rows is too large.

Something like this. Again, as a one-off exercise:

create or replace object num_nt as table of number;

Then your procedure becomes:

PROCEDURE DELETME (
          O_CURSOR         OUT tCursor,
          COD_ERROR        OUT NUMBER,
          MSM_ERROR        OUT VARCHAR2
          )
     AS
    local_nt num_nt;
BEGIN
    select 1 
    bulk collect into local_nt
    from dual;
    COD_ERROR := 1;
    OPEN o_cursor FOR
           select * from table(local_nt);
EXCEPTION 
    WHEN OTHERS THEN
              COD_ERROR:=0;
              MSM_ERROR:=dbms_utility.format_error_backtrace ||' '||SQLERRM;
END DELETME;

There is a third "solution" which is to use dynamic SQL for all the calls in the procedure. This is a really bad approach (even apart from misunderstanding the use of global temporary tables). Dynamic code is flakier than regular code, and should only be used when really necessary. Executing DDL is expensive and not something to be done as part of standard business processing; also it complicates transactions.




回答2:


You can not compile procedure because table does not exist yet. You can either:

  • Create before creating the procedure (create table outside of procedure)
  • Use dynamicaly created SQL in procedure like
    execute immediate 'insert into my_temp_table(column1) values (:x)' using 1. 
    

    Oracle docs about EXECUTE IMMEDIATE http://docs.oracle.com/database/122/LNPLS/EXECUTE-IMMEDIATE-statement.htm#LNPLS01317

    NOTE: Written from head, not tested



    来源:https://stackoverflow.com/questions/41689135/how-to-create-a-temporary-table-inside-stored-procedure-before-opening-a-cursor

  • 易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
    该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!