oracle - 如何在打开游标之前在存储过程中创建临时表?

标签 oracle stored-procedures plsql

我正在尝试在存储过程中创建一个临时表以对其执行多个操作,然后执行 select 语句。我还没有成功使用全局临时表。当我执行下面的代码时,我收到 ORA-00942 错误(表或 View 不存在)。我必须返回光标才能在 Spring 中使用它。有什么想法吗?

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;

最佳答案

您的过程将无法编译,因为您对 TEMP_TABLE 的依赖项不存在。因此就有了ora-00942。当然,如果它确实存在,那么您的过程在运行时失败:创建全局临时表调用将失败,因为该表已经存在。

基本上您误解了 Oracle 中全局临时表的用途。它们是永久性结构,只是它们所保存的数据是暂时的。这是一个常见问题,特别是对于熟悉 SQL Server 并尝试将 T-SQL 转换为 Oracle 的人来说。 MSSQL 中的临时表更像是 PL/SQL 集合。

显然,您发布的代码是一个玩具,因此不清楚为什么您认为需要一个临时表。您很可能不知道,因为 Oracle SQL 非常强大。您可能只需为复杂的 SELECT 语句打开引用游标即可。

但如果您确实有一些真正的需要,那么该怎么做:

首先,作为一次性练习:

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

注意表空间子句:GTT 写入磁盘而不是内存,这意味着它们的填充和读取速度都很慢。如果您要使用 GTT,最好为它们专门设置一个临时表空间,因为与其他临时进程(例如排序)相比,它们具有不同的使用情况。

无论如何,你的程序变成

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;

请记住,您需要发出提交(或回滚)来清除表;如果您不对其进行管理,则当同一 session 重复使用它时可能会产生问题。

或者,使用集合。集合要快得多,因为它们是内存结构。虽然内存来自 session 分配,但如果总行数太大,这不是最佳解决方案。

类似这样的事情。再次强调,作为一次性练习:

create or replace object num_nt as table of number;

那么你的程序就变成了:

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;

还有第三种“解决方案”,即对过程中的所有调用使用动态 SQL。这是一个非常糟糕的方法(即使不包括误解全局临时表的使用)。动态代码比常规代码更不稳定,并且只应在真正必要时使用。执行 DDL 的成本很高,不应该作为标准业务处理的一部分来执行;它还使交易变得复杂。

关于oracle - 如何在打开游标之前在存储过程中创建临时表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41689135/

相关文章:

sql - 动态情况下

oracle - 为什么 Oracle Sql*Plus 会打印很多不需要的标题?

mysql - 尝试在 Mysql 中创建存储过程时出现语法错误?

function - 如何在另一个返回 Refcursor 集的函数中使用 postgresql 函数

Oracle DB (PL/SQL) 重构工具

sql - Oracle 内置函数元数据

oracle - 使用 Oracle 调用接口(interface) (OCI) 设置操作系统用户

mysql - 使用存储过程将数据从 csv 导入到 Mysql 表

sql - Oracle PL/SQL 序列未按对象构造函数的预期递增

sql - 如何从开始到今天每 30 天生成一个列表周期