为什么 PLSQL 比 SQL*Plus 慢

发布于 2024-08-10 08:47:27 字数 411 浏览 5 评论 0原文

我有几个 Oracle 查询在通过 SQL*PLUS 运行时表现良好。然而,当它们作为 PL/SQL 包的一部分执行时,它们需要更长的时间。

我们的 DBA 观察到这些查询通过 PLSQL 需要 10 分钟,通过 SQL*Plus 需要 10 秒。

有人知道在哪里寻找错误配置吗?

客户端 - Windows 2000 服务器 - Linux (Oracle Enterprise)

谢谢

--

解决方案:

我希望我能够接受每个人的答案。其中有几个非常有帮助。

  • 查询正在转换数据类型。
  • 执行计划不匹配。 (提示修复了这个问题。)
  • DBA 正在查看光标所在的时间 打开而不是查询时间。

I have several Oracle queries that perform well when run through SQL*PLUS. However when they are executed as a part of a PL/SQL package, they take MUCH longer.

Our DBA has watched these queries take 10 minutes through PLSQL and 10 seconds through SQL*Plus.

Does anybody have any pointers on where to look for the misconfiguration?

Client - Windows 2000
Server - Linux (Oracle Enterprise)

Thanks

--

Resolution:

I wish I could have accepted everyone's answers. Several of them were quite helpful.

  • The query was converting data types.
  • The execution plans didn't match.
    (Hints fixed that.)
  • The DBA was looking at the time the cursor was
    open instead of the query time.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

眼角的笑意。 2024-08-17 08:47:27

使用 SQL 跟踪来查看每种情况下的执行计划。想到的一种可能性(根据经验):包是否将错误类型的值绑定到查询?可能在 SQL Plus 中您正在运行:

select * from mytable where id = '1234';

但在 PL/SQL 中您正在运行:

select * from mytable where id = p_id;

p_id 被定义为数字。这将在 ID 列上强制使用 TO_NUMBER 并阻止 Oracle 使用索引。

Use SQL trace to see what the execution plans are in each case. One possibility that springs to mind (from experience): is the package binding the wrong type of values to the query? It could be that in SQL Plus you are running:

select * from mytable where id = '1234';

but in PL/SQL you are running:

select * from mytable where id = p_id;

with p_id being defined as a number. That will force a TO_NUMBER on the ID column and prevent Oracle using the index.

走野 2024-08-17 08:47:27

最有可能的情况是,运行时间较长的不是查询,而是在 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

或者当触发器触发时。

Most probably, it's not the queries that run longer but the overhead to process them in PL/SQL.

When you process the query results in a PL/SQL script, a context switch occurs. It requires to pass loads of data between Oracle processes and is quite slow.

Like this code:

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;

runs for more than 3 seconds on my machine, while this one:

SELECT  SUM(1)
FROM    dual
CONNECT BY
        level <= 1000000

completes in only 0.5 seconds.

The context switch also occurs when you call PL/SQL from SQL, like this:

SELECT  plsql_function(column)
FROM    mytable

or when a trigger fires.

浅笑轻吟梦一曲 2024-08-17 08:47:27

我们的 DBA 观察到这些查询通过 PLSQL 需要 10 分钟,通过 PL/PSQL 需要 10 秒。

如果 DBA 不想为您解决这个问题,我可以理解,但如果您的 DBA 确实看到了这两种情况,并且还没有为您提供这两种情况的解释计划,那么他确实不是一个很好的 DBA。

可能没有错误配置,我自己就遇到过这种情况 - 所有绑定变量,没有常量,没有提示。直接运行——性能良好。把它放在 BEGIN..END 里面 - 砰,慢得要命。事实证明,有时查询只是使用 PL/SQL(即 Oracle 9.2)中的不同执行计划。

我的解决方案 - 使用提示,直到 PL/SQL 版本使用与 SQL 相同的计划。

其他可能的问题:

  1. SQL*Plus 仅返回前 100 个或
    所以行然后等待你要求更多,但是 PL/SQL 必须处理
    他们都无需询问。微不足道的问题,但有时会被忽视。
  2. 对于 SQL*Plus 使用常量,对于 PL/SQL 使用绑定变量。有时,使用常量允许优化器检查倾斜数据,并且可以使用其他索引。

Our DBA has watched these queries take 10 minutes through PLSQL and 10 seconds through PL/PSQL.

I could understand if DBA wouldn't want to solve this issue for you but if your DBA really has seen both occurences and has not provided you with explain plans for both cases yet, then he really is not a very good DBA.

There probably is no misconfiguration, I've had it happen on myself - all bind variables, no constants, no hints. Run it directly - good performance. Put it inside BEGIN..END - bam, slow as hell. Turned out that sometimes queries just use different execution plans from within PL/SQL (that was Oracle 9.2).

My solution - used hints until PL/SQL version used the same plan as SQL.

Other possible issues:

  1. SQL*Plus returns only first 100 or
    so rows and then waits for you to ask for more, but PL/SQL has to process
    them all without asking. Trivial issue but sometimes overlooked.
  2. You use constants for SQL*Plus and bind variables for PL/SQL. Sometimes using constants allows optimizer to check for skewed data and it could use some other index.
扶醉桌前 2024-08-17 08:47:27

您真的在这里进行同类比较吗?您是在 PL/SQL 中执行原始 SQL 语句(最佳情况),还是使用显式或隐式游标返回值然后处理它们?有很大的不同。

Are you truly comparing like-for-like here? Are you executing raw SQL statements in PL/SQL (the optimum case) or are you using explicit or implicit cursors to return values and then process them? There's a big difference.

°如果伤别离去 2024-08-17 08:47:27

我们遇到了类似的问题。更新查询在 PL/SQL 块中使用时运行速度非常慢 17 分钟,而在 PL/SQL 之外使用时执行速度非常快(不到 2 秒)。

我们发现PL/SQL中使用的执行计划是不同的。

使用“alter systemlushshared_pool”为我们解决了这个问题。它似乎迫使 PL/SQL 重新考虑要使用的执行计划。

We faced a similar issue. An update query was running very slowly 17 Minutes when used in PL/SQL block and executing very fast (less than 2 seconds) when used outside PL/SQL.

We discovered that the execution plan used in PL/SQL was different.

Using "alter system flush shared_pool" solved the issue for us. It seemed to force PL/SQL to reconsider the execution plan to use.

甜嗑 2024-08-17 08:47:27

引用并扩展 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 速度。

To quote and extend Quassnoi:

Most probably, it's not the queries that run longer but the overhead to process them in PL/SQL.

When you process the query results in a PL/SQL script, a context switch occurs. It requires to pass loads of data between Oracle processes and is quite slow.

Like this code:

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;

runs for more than 3 seconds on my machine, while this one:

SELECT  SUM(1)
FROM    dual
CONNECT BY
        level <= 1000000

completes in only 0.5 seconds.

The context switch also occurs when you call PL/SQL from SQL, like this:

SELECT  plsql_function(column)
FROM    mytable
or when a trigger fires.

One way to solve the context switch problem is to use a BULK COLLECT. If you are collecting a lot of rows, using BULK COLLECT INTO a collection of some type can dramatically speed up SQL in PL/SQL statements.

信仰 2024-08-17 08:47:27

通过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),但也要检查执行计划并进行单元测试。

DML (e.g. SELECT, UPDATE, DELETE) issued through SQLPlus is issued directly to Oracle's SQL engine whereas DML in a PLSQL procedure is first processed by PL/SQL (e.g to do variable bindings) and then sent to the SQL engine.

For the most part the same statement in PL/SQL will perform the same as SQL and both ways will usually produce the same execution plan. In my experience (usually when binding of variables is required) it can cause very different performance. I have seen times where a SELECT issued in SQL Plus takes a fraction of a second while a SELECT issued through PL/SQL takes 1-2 minutes.

I recommend you tune your statement so it works just as well in PL/SQL as it does in SQL. Focus on binding variables correctly (using FORALL and BULK COLLECT) but also examine the execution plans and do unit tests.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文