Jus*_*ave 18
在Oracle中,有一个SQL虚拟机(VM)和一个PL/SQL VM.当您需要从一个VM移动到另一个VM时,会产生上下文转换的成本.单独地,这些上下文转换相对较快,但是当您进行逐行处理时,它们可以累计占用代码花费的大部分时间.当您使用批量绑定时,您可以通过单个上下文切换将多行数据从一个VM移动到另一个VM,从而显着减少上下文切换次数,从而加快代码运行速度.
以一个显式游标为例.如果我写这样的东西
DECLARE
CURSOR c
IS SELECT *
FROM source_table;
l_rec source_table%rowtype;
BEGIN
OPEN c;
LOOP
FETCH c INTO l_rec;
EXIT WHEN c%notfound;
INSERT INTO dest_table( col1, col2, ... , colN )
VALUES( l_rec.col1, l_rec.col2, ... , l_rec.colN );
END LOOP;
END;
Run Code Online (Sandbox Code Playgroud)
那么每次我执行抓取,我都是
每次我插入一行,我都在做同样的事情.我承担了上下文转换的成本,将一行数据从PL/SQL VM发送到SQL VM,要求SQL执行该INSERT语句,然后将另一个上下文转换回来的成本转回PL/SQL.
如果source_table有100万行,那就是400万个上下文移位,这可能占我代码经过时间的合理分数.另一方面,如果我BULK COLLECT使用LIMIT100的100,我可以通过从SQL VM中检索100行数据到每次我承担上下文成本的PL/SQL集合中来消除99%的上下文转换每次我在那里进行上下文转换时,移动并插入100行到目标表中.
如果可以重写我的代码以使用批量操作
DECLARE
CURSOR c
IS SELECT *
FROM source_table;
TYPE nt_type IS TABLE OF source_table%rowtype;
l_arr nt_type;
BEGIN
OPEN c;
LOOP
FETCH c BULK COLLECT INTO l_arr LIMIT 100;
EXIT WHEN l_arr.count = 0;
FORALL i IN 1 .. l_arr.count
INSERT INTO dest_table( col1, col2, ... , colN )
VALUES( l_arr(i).col1, l_arr(i).col2, ... , l_arr(i).colN );
END LOOP;
END;
Run Code Online (Sandbox Code Playgroud)
现在,每次执行fetch时,我都会通过一组上下文切换将100行数据检索到我的集合中.每次我进行FORALL插入操作时,我都会插入100行并使用一组上下文移位.如果source_table有100万行,这意味着我已经从400万个上下文转换到40,000个上下文转换.如果上下文转换占我的代码经过时间的20%,我已经消除了19.8%的经过时间.
您可以增加大小LIMIT以进一步减少上下文转换的数量,但您很快就会达到收益递减规律.如果使用的LIMIT是1000而不是100,那么你将消除99.9%的背景变化,而不是99%.这意味着你的收藏品使用了10倍以上的PGA内存.在我们的假设示例中,它只会消耗0.18%的经过时间.通过消除额外的上下文转换,您很快就可以达到使用额外内存所节省的时间.一般来说,LIMIT在100到1000之间的某个地方可能是最佳选择.
当然,在这个例子中,消除所有上下文转换并在单个SQL语句中执行所有操作仍然会更有效
INSERT INTO dest_table( col1, col2, ... , colN )
SELECT col1, col2, ... , colN
FROM source_table;
Run Code Online (Sandbox Code Playgroud)
如果您正在对源表中的数据进行某种操作(在SQL中无法合理地实现),那么首先使用PL/SQL是有意义的.
另外,我故意在我的例子中使用了一个显式游标.如果您是使用隐式游标,在最新版本的Oracle,你得到的好处BULK COLLECT与LIMIT的隐式.还有另一个StackOverflow问题讨论了隐式和显式游标与批量操作的相对性能优势,这些操作可以更详细地了解这些特定的皱纹.
| 归档时间: |
|
| 查看次数: |
16328 次 |
| 最近记录: |