包中绑定与普通SQL的动态SQL性能

时间:2009-01-05 18:39:40

标签: performance oracle

在Oracle 10g环境中,我有一个语句需要根据游标的结果执行数百万次。为了灵活性,当前代码将语句作为包体中的常量。这是代码的简化版本。请记住,这些都在同一个包装内:

c_Stmt  CONSTANT VARCHAR2(128) := 'DELETE FROM t WHERE fldA = :b1';

...

PROCEDURE p1(vSomeNumber NUMBER(10)) IS
BEGIN
  EXECUTE IMMEDIATE c_Stmt USING vSomeNumber;
END;

...

FOR i IN 1 .. 9999999
  LOOP
     p1(i);
  END LOOP;

由于动态SQL已经使用了绑定变量,因此重写此代码以使用常规DML语句替换动态SQL是否有任何性能优势:

PROCEDURE p1(vSomeNumber NUMBER(10)) IS
BEGIN
  /* EXECUTE IMMEDIATE c_Stmt USING vSomeNumber; */
  DELETE FROM t WHERE fldA = vSomeNumber;
END;

我认为影响可能会对解析的数量产生影响,但我不清楚这是否仍然是10g中的一个问题。

感谢您的回答。

5 个答案:

答案 0 :(得分:2)

常规SQL比动态SQL(执行立即...)更快但是为什么要逐个删除行?为什么不删除行的一个大删除语句?思考基于集合,而不是逐行!

答案 1 :(得分:1)

通常,在循环之前准备语句更有效,并在循环期间执行语句句柄(此时提供参数)。这样就可以避免每次循环时解析和准备语句的开销。

使用静态SQL将避免解析步骤,因为这应该在您定义过程时完成。但这并不能避免准备步骤的开销。

无论如何,互联网上的人们几乎总是无法确定哪种方式会表现得更好。这部分取决于您的数据库和数据。您应该尝试两种方式并自己衡量绩效。

答案 2 :(得分:1)

第二种方法(使用Delete语句)比第一种方法(使用Execute Immediate)更快的原因之一是上下文切换。如果执行Execute Immediate,PL / SQL进程必须停止,Oracle需要加载解析进程,解析语句(或从语句缓存中检索它),然后再切换到执行进程。使用后者,可以减少一个上下文切换。

这些上下文切换(如多线程程序中的线程切换)非常耗时。这就是为什么Oracle花费大量时间来使批量处理语句(批量插入,批量选择)在PL / SQL中正常工作的原因。

如上所述,尝试让PL / SQL一次性完成所有删除操作,例如通过在PL / SQL表中创建要删除的ID列表,并在FORALL循环中执行删除。更少的上下文切换意味着更快地完成更多工作

答案 3 :(得分:1)

根据具体需求,您可以使用多种方法来提高删除速度。

首先,将一堆ID存储在PL / SQL数组中,并定期(最后)在(:arr(1),:arr(2)中执行FORALL数组DELETE或DELETE FROM表WHERE ID ...)

其次,将ID存储在GLOBAL TEMPORARY TABLE中并定期/最后,执行DELETE FROM表WHERE id IN(SELECT id FROM global_temp_tbl)

这两个选项显然会延迟删除步骤,因此可能会影响使用这些表的循环内的SQL。

第三,切换到静态SQL。我没有看到动态SQL的好处(但您可能已经简化了示例,以便隐藏好处)。切换到静态将减少软解析的数量(Oracle散列语句并在其语句高速缓存中查找匹配)。在您的情况下,节省可能会或可能不会很重要。它主要是CPU时间,但如果你有其他进程在同一时间运行,可能会有一些锁定等待时间。

第四,如果软解析开销很大,但是有一个原因你不想切换到静态SQL,则可以使用DBMS_SQL。在那里,DBMS_SQL.PARSE将在循环之外,并且只有BIND_VARIABLE和EXECUTE组件才会在循环内。

答案 4 :(得分:-2)

由于你被困在pl / sql地狱中,另一种选择是更新行,以表明它应该被删除。

您当前的查询可以忽略这些“软删除”行。

在将来的某个维护窗口中,您可以使用简单的删除语句删除“软删除”行。