我在11g和12c Oracle系统上运行。我有一些关于我即将编写的PL / SQL的问题。我一直在广泛研究,我没有看到答案,并希望从论坛获得一些意见。我的主要目标是保持高水平的表现。我正在尝试在处理此数据时避免上下文切换。
SP的目的是批量连接来自2个不同表的一组数据,操作集合中的一组字段并将其写入另一个实例中的另一个表。我显然希望尽可能多地在BULK中做,但我不确定,当我完成它时,由于复杂的场操作,我不会最终得到某种单行上下文切换。
详情如下:
声明一个将进行内部连接的游标。在SELECT中我想做一些数据操作。我想调用一个Function来执行此操作,因为尝试在select语句中执行它会非常麻烦:
CURSOR c1 is
SELECT DISTINCT A.ID,
A.PT_NBR,
A.PT_DT,
A.PT_QTY,
(COMPLX_CALC_FUNCTION(B.TR_TM,B.TR_CD ) nRESULT
FROM PT_TABLE A
INNER JOIN TRAN_TABLE B
ON (A.PT_NBR = B.TRAN_NBR
AND A.PT_DT = B.TRAN_DT
AND A.PT_DT BETWEEN B.START_DT AND B.END_DT)
WHERE A.ID = vID;
结果将是数十万或数百万条记录,因此我想批量进行
BEGIN
OPEN c1;
LOOP
FETCH c1 BULK COLLECT INTO vTable LIMIT 20000;
---body code
-- trying to avoid additional manipulation of the data here but there may still be some
FORALL indx IN vTable.FIRST .. vTable.LAST
...
INSERT INTO Table2 VALUES vTable(indx);
--ending stuff
;
COMPLX_CALC_FUNCTION
将接受输入并执行数学运算并调用其他函数,如NVL,SUM和CEIL。
所以,问题是......由于光标选择中的函数调用或者我是否需要操作SP体中的数据,我是否会受上下文切换的影响?另外,在这种情况下我还需要考虑其他任何性能问题吗?我试图避免将数据转储到临时物理表并操纵它,因为这似乎比在内存中执行它要慢得多。您的专家建议表示赞赏。
答案 0 :(得分:1)
在内存循环中调用表中的函数?这将避免SQL-PLSQL上下文切换,这种切换成本更高,特别是因为每行将切换回来。 FORALL循环可以用内存中的表替换内存中的常规for循环。这应该仍然非常快。如果你想使用FORALL进行批量插入,你可以从“for循环”中取出插件,但是为了额外的开销我不认为它会产生很大的不同。
至于批量做所有事情 - 我的建议是,对于数百万行 - 取决于表的“厚度/薄度” - 你肯定会在数据库中吹掉你的PGA和排序区域大小,除非你有一个非常友好的DBA,为您提供所需的所有记忆(可疑)。作为一般规则,我尽量不在内存中放置超过2-5百万行。
declare
l_complex number; -- dont know the real datatype here??
BEGIN
OPEN c1;
LOOP
FETCH c1 BULK COLLECT INTO vTable LIMIT 20000;
---body code
-- trying to avoid additional manipulation of the data here but there may still be some
FOR indx IN vTable.FIRST .. vTable.LAST loop
l_complex : = COMPLX_CALC_FUNCTION(vtable(indx).TR_TM,vtable(indx).TR_CD );
INSERT INTO Table2 VALUES vTable(indx);
end loop;
...
--ending stuff
;
答案 1 :(得分:1)
嘿你好。只是有机会查看您的查询。我会怎么做 建议做一个简单的测试。在我看来,这是可能的 如果可以避免任何上下文切换,总是使用SQL。以下片段肯定会帮助您理解这一点。希望它有所帮助
set serveroutput on;
DECLARE
lv_num DBMS_SQL.NUMBER_TABLE;
lv_t1 PLS_INTEGER;
lv_t2 PLS_INTEGER;
BEGIN
lv_t1:=dbms_utility.get_time();
SELECT LEVEL bulk collect INTO lv_num FROM DUAL CONNECT BY LEVEL < 1000000;
FORALL I IN lv_num.FIRST..lv_num.LAST
INSERT INTO NUM_TAB VALUES
(lv_num(I)
);
lv_t2:=dbms_utility.get_time();
dbms_output.put_line(lv_t2-lv_t1||' '||' Bulk collect');
lv_t1:=dbms_utility.get_time();
INSERT INTO NUM_TAB
SELECT LEVEL FROM DUAL CONNECT BY LEVEL < 1000000;
lv_t2:=dbms_utility.get_time();
dbms_output.put_line(lv_t2-lv_t1||' '||' SQL Statement');
END;
-------------------------------OUTPUT--------------------------------------
PL/SQL procedure successfully completed.
16493 Bulk collect
1475 SQL Statement
-----------------------------------------------------------------------------
答案 2 :(得分:0)
对于表演,你有两件事
- select语句:为此,您可以检查select语句的执行计划并调整select语句。
醇>2.您的insert语句看起来非常精细,因为您在单击时选择20000行(LIMIT 20000)并将其插入另一行 带有批量插入的桌子。通过这种方式,您将有一个上下文切换 对于20000行,也没有内存不足的问题。
在这种情况下你只需要调整你的select语句。