在oracle中避免全局临时表的方法

Omn*_*ent 22 oracle plsql temp-tables

我们刚刚将我们的sql server存储过程转换为oracle程序.Sql Server SP高度依赖于会话表(INSERT INTO #table1...)这些表在oracle中被转换为全局临时表.我们最终为我们的400 SP提供了500 GTT

现在我们发现,由于性能和其他问题,在Oracle中使用GTT是最后的选择.

有什么其他选择吗?收藏?游标?

我们对GTT的典型使用方式如下:

插入GTT

INSERT INTO some_gtt_1
  (column_a,
   column_b,
   column_c)
  (SELECT someA,
      someB,
      someC
     FROM TABLE_A
    WHERE condition_1 = 'YN756'
      AND type_cd = 'P'
      AND TO_NUMBER(TO_CHAR(m_date, 'MM')) = '12'
      AND (lname LIKE (v_LnameUpper || '%') OR
      lname LIKE (v_searchLnameLower || '%'))
      AND (e_flag = 'Y' OR
      it_flag = 'Y' OR
      fit_flag = 'Y'));
Run Code Online (Sandbox Code Playgroud)

更新GTT

UPDATE some_gtt_1 a
SET column_a = (SELECT b.data_a FROM some_table_b b 
               WHERE a.column_b = b.data_b AND a.column_c = 'C')
WHERE column_a IS NULL OR column_a = ' ';
Run Code Online (Sandbox Code Playgroud)

然后从GTT获取数据.这些只是示例查询,实际上查询实际上是带有大量连接和子查询的补充.

我有三个问题:

  1. 有人可以展示如何将上面的示例查询转换为集合和/或游标吗?
  2. 由于使用GTT,您可以使用SQL本地工作......为什么要远离GTT?他们真的那么糟糕吗?
  3. 关于什么时候使用以及何时避免使用GTT的准则应该是什么

APC*_*APC 30

让我们先回答第二个问题:

"为什么要离开GTT?他们真的那么糟糕."

几天前,我敲了一个概念证明,它将一个较大的XML文件(~18MB)加载到XMLType中.因为我不想永久存储XMLType,所以我尝试将其加载到PL/SQL变量(会话内存)和临时表中.将其加载到临时表中所花费的时间是将其加载到XMLType变量(5秒与1秒相比)的五倍.区别在于临时表不是内存结构:它们被写入磁盘(特别是您指定的临时表空间).

如果你想缓存大量数据然后将它存储在内存中会对PGA造成压力,如果你有很多会话,那就不好了.所以这是RAM和时间之间的权衡.

对第一个问题:

"有人可以展示如何将上面的示例查询转换为集合和/或游标吗?"

您发布的查询可以合并为一个语句:

SELECT case when a.column_a IS NULL OR a.column_a = ' ' 
           then b.data_a
           else  column_a end AS someA,
       a.someB,
       a.someC
FROM TABLE_A a
      left outer join TABLE_B b
          on ( a.column_b = b.data_b AND a.column_c = 'C' )
WHERE condition_1 = 'YN756'
  AND type_cd = 'P'
  AND TO_NUMBER(TO_CHAR(m_date, 'MM')) = '12'
  AND (lname LIKE (v_LnameUpper || '%') OR
  lname LIKE (v_searchLnameLower || '%'))
  AND (e_flag = 'Y' OR
  it_flag = 'Y' OR
  fit_flag = 'Y'));
Run Code Online (Sandbox Code Playgroud)

(我只是简单地改变了你的逻辑,但这个case()陈述可以用一个整理者来代替nvl2(trim(a.column_a), a.column_a, b.data_a)).

我知道你说你的查询更复杂,但你的第一个停靠点应该是考虑重写它们.我知道将一个粗略的查询分解成许多与PL/SQL结合在一起的婴儿SQL是多么诱人,但纯SQL更有效率.

要使用集合,最好在SQL中定义类型,因为它使我们可以灵活地在SQL语句和PL/SQL中使用它们.

create or replace type tab_a_row as object
    (col_a number
     , col_b varchar2(23)
     , col_c date);
/
create or replace type tab_a_nt as table of tab_a_row;
/
Run Code Online (Sandbox Code Playgroud)

这是一个示例函数,它返回一个结果集:

create or replace function get_table_a 
      (p_arg in number) 
      return sys_refcursor 
is 
    tab_a_recs tab_a_nt; 
    rv sys_refcursor; 
begin 
    select tab_a_row(col_a, col_b, col_c)  
    bulk collect into tab_a_recs 
    from table_a 
    where col_a = p_arg; 

    for i in tab_a_recs.first()..tab_a_recs.last() 
    loop 
        if tab_a_recs(i).col_b is null 
        then 
            tab_a_recs(i).col_b :=  'something'; 
        end if; 
    end loop;  

    open rv for select * from table(tab_a_recs); 
    return rv; 
end; 
/ 
Run Code Online (Sandbox Code Playgroud)

在这里,它正在行动:

SQL> select * from table_a
  2  /

     COL_A COL_B                   COL_C
---------- ----------------------- ---------
         1 whatever                13-JUN-10
         1                         12-JUN-10

SQL> var rc refcursor
SQL> exec :rc := get_table_a(1)

PL/SQL procedure successfully completed.

SQL> print rc

     COL_A COL_B                   COL_C
---------- ----------------------- ---------
         1 whatever                13-JUN-10
         1 something               12-JUN-10

SQL>
Run Code Online (Sandbox Code Playgroud)

在函数中,必须使用列实例化类型,以避免ORA-00947异常.填充PL/SQL表类型时,这不是必需的:

SQL> create or replace procedure pop_table_a
  2        (p_arg in number)
  3  is
  4      type table_a_nt is table of table_a%rowtype;
  5      tab_a_recs table_a_nt;
  6  begin
  7      select *
  8      bulk collect into tab_a_recs
  9      from table_a
 10      where col_a = p_arg;
 11  end;
 12  /

Procedure created.

SQL> 
Run Code Online (Sandbox Code Playgroud)

最后,指导方针

"关于何时使用以及何时避免使用GTT的指导原则应该是什么?"

当我们需要在同一会话中的不同程序单元之间共享缓存数据时,全局临时表非常好.例如,如果我们有一个通用的报告结构,它由一个GTT生成的单个函数生成,该GTT由几个程序之一填充.(虽然即使这也可以用动态ref游标实现......)

如果我们有很多中间处理,那么全局临时表也很好,这些处理过于复杂,无法通过单个SQL查询来解决.特别是如果必须将该处理应用于检索到的行的子集.

但一般来说,推定应该是我们不需要使用临时表.所以

  1. 在SQL中执行它,除非它太难了在哪种情况下......
  2. ...在PL/SQL变量(通常是集合)中执行它,除非它占用太多内存,在这种情况下......
  3. ......用全球临时表做