在oracle中避免全局临时表的方法
我们只是将我们的sql服务器存储过程转换为oracle过程。 Sql Server SP的高度依赖于会话表( INSERT INTO #table1...
),这些表在oracle中被转换为全局临时表。 我们结束了对我们400 SP的500 GTT
现在我们发现在oracle中使用GTT是因为性能和其他问题而被认为是最后的select。
还有什么其他的select? collections? 游标?
我们对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'));
更新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 = ' ';
并在稍后从GTT获取数据。 这些只是样本查询,实际上查询是真正完整的连接和子查询。
我有三个部分的问题:
- 有人可以展示如何将上述示例查询转换为集合和/或游标?
- 由于使用GTT,您可以使用SQL本身工作…为什么要离开GTT? 他们真的很糟糕吗?
- 什么时候应该什么时候使用和什么时候避免GTT的
我们先回答第二个问题:
“为什么要离开GTT?他们真的很糟糕。”
几天前,我正在敲一个概念certificate,将一个大型的XML文件(~18MB)加载到一个XMLType中。 因为我不想永久存储XMLType,我尝试将它加载到一个PL / SQLvariables(会话内存)和一个临时表中。 将它加载到一个临时表中的时间是将它加载到一个XMLTypevariables中的5倍(5秒相比1秒)。 不同的是因为临时表不是内存结构:它们被写入到磁盘(特别是你指定的临时表空间)。
如果你想caching大量的数据,那么把它存储在内存中将会给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'));
(我只是换了你的逻辑,但case()
语句可以用nvl2(trim(a.column_a), a.column_a, b.data_a)
)来代替。
我知道你说你的查询比较复杂,但你的第一个端口应该是考虑重写它们。 我知道如何将一个粗糙的查询分解成许多与PL / SQL拼接在一起的婴儿SQL,但纯SQL的效率更高。
要使用集合,最好在SQL中定义types,因为它使我们能够灵活地在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; /
这是一个示例函数,它返回一个结果集:
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; /
在这里它是在行动:
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>
在函数中,为了避免ORA-00947exception,有必要实例化列的types。 填充PL / SQL表types时,这不是必需的:
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>
最后,指导方针
“什么时候应该什么时候使用和什么时候避免GTT的”
当我们需要在同一个会话中的不同程序单元之间共享caching数据时,全局临时表格是非常好的。 例如,如果我们有一个通用的报表结构,它由一个函数生成,并由几个过程之一填充的GTT生成。 (即使这也可以用dynamicref游标来实现…)
如果我们有很多中间处理过程太复杂而无法用单个SQL查询来解决,那么全局临时表也是很好的。 特别是如果这个处理必须被应用到检索行的子集。
但一般来说,这个假设应该是我们不需要使用临时表格。 所以
- 在SQL中这样做,除非它太难了,这种情况下…
- …在PL / SQLvariables(通常是集合),除非它需要太多的内存,这种情况下…
- …用全球临时表来做
通常我会使用PL / SQL集合来存储less量的数据(可能是一千行)。 如果数据量更大,我会使用一个GTT,这样它们不会使进程内存超载。
因此,我可能会从数据库中select几百行到一个PL / SQL集合,然后通过循环来做一些计算/删除或其他任何操作,然后将该集合插入到另一个表中。
如果我正在处理成千上万的行,我会尝试将大量的“繁重”处理推入大型SQL语句中。 这可能需要也可能不需要GTT。
您可以使用SQL级别的集合对象作为SQL和PL / SQL之间很容易转换的对象
create type typ_car is object (make varchar2(10), model varchar2(20), year number(4)); / create type typ_coll_car is table of typ_car; / select * from table (typ_coll_car(typ_car('a','b',1999), typ_car('A','Z',2000))); MAKE MODEL YEAR ---------- -------------------- --------------- ab 1,999.00 AZ 2,000.00 declare v_car1 typ_car := typ_car('a','b',1999); v_car2 typ_car := typ_car('A','Z',2000); t_car typ_coll_car := typ_coll_car(); begin t_car := typ_coll_car(v_car1, v_car2); FOR i in (SELECT * from table(t_car)) LOOP dbms_output.put_line(i.year); END LOOP; end; /