我有几个Oracle查询在运行SQL * PLUS时表现良好。但是,当它们作为PL / SQL包的一部分执行时,它们会花费更长的时间。
我们的DBA已经看过这些查询需要10分钟通过PLSQL,10秒通过SQL * Plus。
是否有人指出在何处寻找错误配置?
客户端 - Windows 2000 服务器 - Linux(Oracle Enterprise)
由于
-
决议:
我希望我能接受每个人的答案。其中有几个非常有帮助。
答案 0 :(得分:9)
使用SQL跟踪查看每种情况下的执行计划。一种可能的想法(来自经验):是否将错误类型的值绑定到查询?可能是在SQL Plus中你正在运行:
select * from mytable where id = '1234';
但是在PL / SQL中你正在运行:
select * from mytable where id = p_id;
将p_id定义为数字。这将强制ID列上的TO_NUMBER并阻止Oracle使用索引。
答案 1 :(得分:3)
最有可能的是,运行时间较长的查询不是PL/SQL
处理它们的开销。
在PL/SQL
脚本中处理查询结果时,会发生上下文切换。它需要在Oracle
个进程之间传递大量数据,而且速度很慢。
喜欢这段代码:
DECLARE
cnt INTEGER := 0;
CURSOR cr_main IS
SELECT 1 AS id
FROM dual
CONNECT BY
level <= 1000000;
BEGIN
FOR res IN cr_main
LOOP
cnt := cnt + res.id;
END LOOP;
DBMS_OUTPUT.put_line(cnt);
END;
在我的机器上运行时间超过3
秒,而这一次:
SELECT SUM(1)
FROM dual
CONNECT BY
level <= 1000000
仅在0.5
秒内完成。
当您从PL/SQL
拨打SQL
时也会发生上下文切换,如下所示:
SELECT plsql_function(column)
FROM mytable
或触发器触发时。
答案 2 :(得分:2)
我们的DBA已经看过这些查询需要10分钟通过PLSQL,10秒通过PL / PSQL。
我能理解DBA是否不想为你解决这个问题但是如果你的DBA确实已经看到了这两个问题并且还没有为你提供这两个案例的解释计划,那么他真的不是一个非常好的DBA。
可能没有错误配置,我已经发生过这种情况 - 所有绑定变量,没有常量,没有提示。直接运行 - 性能良好。把它放在BEGIN..END - bam里面,慢慢地说。事实证明,有时查询只是使用PL / SQL(即Oracle 9.2)中的不同执行计划。
我的解决方案 - 使用提示,直到PL / SQL版本使用与SQL相同的计划。
其他可能的问题:
答案 3 :(得分:1)
你真的在这里比较喜欢吗?您是在PL / SQL中执行原始SQL语句(最佳情况)还是使用显式或隐式游标返回值然后处理它们?这有很大的不同。
答案 4 :(得分:1)
我们遇到了类似的问题。更新查询在PL / SQL块中使用时运行非常缓慢17分钟,并且在PL / SQL外部使用时执行速度非常快(少于2秒)。
我们发现PL / SQL中使用的执行计划是不同的。
使用“alter system flush shared_pool”解决了我们的问题。它似乎迫使PL / SQL重新考虑使用的执行计划。
答案 5 :(得分:0)
通过SQLPlus发出的DML(例如SELECT,UPDATE,DELETE)直接发布到Oracle的SQL引擎,而PLSQL过程中的DML首先由PL / SQL处理(例如,进行变量绑定),然后发送到SQL引擎。
在大多数情况下,PL / SQL中的相同语句将执行与SQL相同的操作,并且这两种方式通常会产生相同的执行计划。根据我的经验(通常在需要绑定变量时),它可能会导致非常不同的性能。我已经看到在SQL Plus中发出的SELECT只需要几分之一秒的时间,而通过PL / SQL发出的SELECT需要1-2分钟。
我建议您调整语句,使其在PL / SQL中的工作方式与在SQL中的工作方式相同。正确关注绑定变量(使用FORALL和BULK COLLECT),同时检查执行计划并进行单元测试。
答案 6 :(得分:0)
引用和扩展Quassnoi:
最有可能的是,运行时间较长的查询不是在PL / SQL中处理它们的开销。
在PL / SQL脚本中处理查询结果时,会发生上下文切换。它需要在Oracle进程之间传递大量数据,而且速度很慢。
喜欢这段代码:
DECLARE
cnt INTEGER := 0;
CURSOR cr_main IS
SELECT 1 AS id
FROM dual
CONNECT BY
level <= 1000000;
BEGIN
FOR res IN cr_main
LOOP
cnt := cnt + res.id;
END LOOP;
DBMS_OUTPUT.put_line(cnt);
END;
在我的机器上运行超过3秒,而这一个:
SELECT SUM(1)
FROM dual
CONNECT BY
level <= 1000000
仅在0.5秒内完成。
从SQL调用PL / SQL时也会发生上下文切换,如下所示:
SELECT plsql_function(column)
FROM mytable
or when a trigger fires.
解决上下文切换问题的一种方法是使用BULK COLLECT。如果要收集大量行,使用BULK COLLECT INTO某种类型的集合可以大大加快PL / SQL语句中的SQL。