使用批量收集的显式游标与隐式游标:任何性能问题?

Explicit cursors using bulk collect vs. implicit cursors: any performance issues?

在 Oracle Magazine 的一篇较早的文章中(现在在线为 On Cursor FOR Loops)Steven Feuerstein 展示了使用 bulk collect 显式 cursor for loops 的优化(在线文章中的列表 4):

DECLARE
  CURSOR employees_cur is SELECT * FROM employees;
  TYPE employee_tt IS TABLE OF employees_cur%ROWTYPE INDEX BY PLS_INTEGER;
  l_employees   employee_tt;
BEGIN
  OPEN employees_cur;
  LOOP
    FETCH employees_cur BULK COLLECT INTO l_employees LIMIT 100;
    -- process l_employees  using pl/sql only
    EXIT WHEN employees_cur%NOTFOUND;
  END LOOP;
  CLOSE employees_cur;
END;

我知道 bulk collect 提高了性能,因为 SQL 和 PL/SQL 之间的上下文切换较少。

我的问题是关于隐式 cursor for loops:

BEGIN
  FOR S in (SELECT * FROM employees)
  LOOP
    -- process current record of S
  END LOOP;
END;

每个记录的每个循环中是否有上下文切换?问题与显式游标相同还是以某种方式优化了 "behind the scene"?使用带有批量收集的显式游标重写代码会更好吗?

是的,即使你的 -- process current record of S 包含纯 SQL 而没有 PL/SQL 你有上下文切换,因为 FOR ... LOOP 是 PL/SQL 但查询是SQL.

只要有可能,您应该更喜欢使用单个 SQL 语句处理数据(还可以考虑 MERGE,而不仅仅是 DELETE、UPDATE、INSERT),在大多数情况下,它们比一行更快-逐行处理。

请注意,如果您通过 l_employees 进行循环并为每条记录执行 DLL,您将不会获得任何性能。

LIMIT 100 没什么用。一次只处理 100 行几乎与逐行处理相同 - Oracle 在具有 64K 内存的 Z80 上不运行。

从 Oracle 10g 开始,优化 PL/SQL 编译器可以自动将 FOR 循环转换为默认数组大小为 100 的 BULK COLLECT 循环。

所以通常不需要将隐式 FOR 循环转换为 BULK COLLECT 循环。

但有时您可能想改用 BULK COLLECT。例如,如果每次提取 100 行的默认数组大小不满足您的要求,或者如果您更愿意更新集合内的数据。

Tom Kyte 回答了同样的问题。您可以在这里查看:Cursor FOR loops optimization in 10g