有没有办法让Oracle为每次查询调用重新计算查询计划?

发布于 2024-07-16 17:36:52 字数 140 浏览 5 评论 0原文

我有一个参数化查询。 根据参数值的不同,最佳查询计划差异很大。 问题在于:Oracle 将第一次查询调用中的计划用于后续调用,从而导致性能不佳。 我通过动态SQL来处理它,但这种方式远非优雅。 那么问题来了:有没有办法告诉Oracle必须重新计算查询计划呢?

I have a parameterized query. Depending on parameter values optimal query plan varies significantly. Here is the trouble: Oracle uses the plan from the first query invocation for subsequent invocations resulting in bad performance. I deal with it by dynamic SQL but this way is far from elegant. So the question is: is there a way to tell Oracle that the query plan must be recalculated?

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

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

发布评论

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

评论(7

没︽人懂的悲伤 2024-07-23 17:36:52

如果查询计划确实在参数值上发生了显着变化,也许您不应该为此参数使用绑定变量。

该参数可以采用多少个不同的值? 如果只有几个,您最终会得到几个查询计划(每个值一个),并且这些计划有望表现良好并且可以重复使用。

或者您可以在 SQL 语句中使用注释“/* THIS IS VALUE BRACKET ONE * /”来分隔它们(或查询分析器提示,如果您觉得您知道哪些是合适的,则可能适用 /*+ CARDINALITY */ 之类的内容这里)。

不管怎样,我认为你想要有单独的 SQL 语句,以便你可以在 Statspack 和朋友中获得单独的报告,因为看起来你真的想微调该查询。

If the query plan really changes significantly on the parameter value, maybe you should not use bind variables for this parameter.

How many different values can that parameter take? If there are only a few, you would end up with a couple of query plans (one for each value), and those would hopefully perform well and can be re-used.

Or you could use comments "/* THIS IS VALUE BRACKET ONE * /" in the SQL statement to separate them (or query analyzer hints, if you feel like you know which ones are appropriate, something like /*+ CARDINALITY */ might apply here).

Either way, I think you want to have separate SQL statements so that you can get separate reporting in Statspack and friends, because it looks like you really want to fine-tune that query.

并安 2024-07-23 17:36:52

对于 Oracle 10g,我们将选择查询中的任何表并执行。

GRANT SELECT ON table1 TO user1;

这将使引用该表的任何查询的计划无效。 当然,您会希望选择一个对其他查询影响最小的表。 另请参阅此页面了解更多信息和一个示例列表。

For Oracle 10g we would choose any table in the query and execute

GRANT SELECT ON table1 TO user1;

This would invalidate the plan of any query referencing this table. Of course you would want to choose a table which has minimal impact on other queries. See also this page for more information and a sample listing.

幽蝶幻影 2024-07-23 17:36:52

如果您确实想每次生成一个新的查询计划,只需按照 thilo 的建议添加一个唯一的注释

select /* SQLID=1234 */ 1 from dual;
select /* SQLID=1235 */ 1 from dual;

即可。这些应该会生成唯一的计划。

不过,我非常怀疑是否需要这样做,在尝试解决优化器之前,您应该非常确定您的统计数据没有错误。

If you really want to generate a new query plan each time, just put a unique comment in as thilo suggests

select /* SQLID=1234 */ 1 from dual;
select /* SQLID=1235 */ 1 from dual;

These should generate unique plans.

I'd be highly suspicious of the need to do this though, before trying to work around the optimiser, you should be very sure your stats aren't wrong.

东京女 2024-07-23 17:36:52

优化器使用的内容之一是相关列上的直方图。 如果您使用绑定变量并且相关列上有直方图,则计划可能会根据参数值而变化。 第一个计划将保留在共享池中,并将用于所有值。

如果您不希望这样,那么您可以使用文字而不是绑定(如果您不会有太多版本的同一 sql)。 或者您可以删除直方图,删除直方图可确保生成与绑定参数值无关的相同计划。

每次执行都使 SQL 无效并不是一个好主意。 根据此 SQL 的使用频率,它可能会导致新问题,例如由硬解析引起的闩锁问题。

One of the things the optimizer uses is histograms on the related columns. If you are using a bind variable and if you have histograms on the related column the plan may change depending on the parameter value. This first plan will stay in the shared pool and will be used for all values.

If you do not want this then you can use literals instead of binds (if you will not have too many versions of the same sql). Or you can remove the histogram, removing the histogram ensures that independent of the bind parameter value the same plan will be generated.

Invalidating the sql for every execution is not a good idea. Depending on how often this sql is used it may cause new problems like latch problems caused by hard parsing.

枫以 2024-07-23 17:36:52

有没有办法告诉Oracle必须重新计算查询计划?

您可以为不同的执行计划创建多个 OUTLINE,并使用 OUTLINE CATEGORIES 选择要使用的一个:

CREATE OUTLINE ol_use_nl
FOR
SELECT  *
FROM    mytable1 mt1
JOIN    mytable2 mt2
ON      mt1.id = mt2.id
WHERE   mt1.value BETWEEN :a AND :b
CATEGORY FILTERED;

/* Edit the outline to add USE_NL */

CREATE OUTLINE ol_use_nl
FOR
SELECT  *
FROM    mytable1 mt1
JOIN    mytable2 mt2
ON      mt1.id = mt2.id
WHERE   mt1.value BETWEEN :a AND :b
CATEGORY UNFILTERED;

/* Edit the outline to add USE_HASH */

ALTER SESSION SET USE_STORED_OUTLINES = FILTERED;

SELECT  *
FROM    mytable1 mt1
JOIN    mytable2 mt2
ON      mt1.id = mt2.id
WHERE   mt1.value BETWEEN 1 AND 2

/* This will use NESTED LOOPS */

ALTER SESSION SET USE_STORED_OUTLINES = UNFILTERED;

SELECT  *
FROM    mytable1 mt1
JOIN    mytable2 mt2
ON      mt1.id = mt2.id
WHERE   mt1.value BETWEEN 1 AND 1000000

/* This will use HASH JOIN */

Is there a way to tell Oracle that the query plan must be recalculated?

You may create several OUTLINE's for different execution plans and select which one to use using OUTLINE CATEGORIES:

CREATE OUTLINE ol_use_nl
FOR
SELECT  *
FROM    mytable1 mt1
JOIN    mytable2 mt2
ON      mt1.id = mt2.id
WHERE   mt1.value BETWEEN :a AND :b
CATEGORY FILTERED;

/* Edit the outline to add USE_NL */

CREATE OUTLINE ol_use_nl
FOR
SELECT  *
FROM    mytable1 mt1
JOIN    mytable2 mt2
ON      mt1.id = mt2.id
WHERE   mt1.value BETWEEN :a AND :b
CATEGORY UNFILTERED;

/* Edit the outline to add USE_HASH */

ALTER SESSION SET USE_STORED_OUTLINES = FILTERED;

SELECT  *
FROM    mytable1 mt1
JOIN    mytable2 mt2
ON      mt1.id = mt2.id
WHERE   mt1.value BETWEEN 1 AND 2

/* This will use NESTED LOOPS */

ALTER SESSION SET USE_STORED_OUTLINES = UNFILTERED;

SELECT  *
FROM    mytable1 mt1
JOIN    mytable2 mt2
ON      mt1.id = mt2.id
WHERE   mt1.value BETWEEN 1 AND 1000000

/* This will use HASH JOIN */
无法回应 2024-07-23 17:36:52

您的问题是由于绑定变量窥视 - 为整个数据库关闭它可能会破坏其他事情,但您可以通过添加以下提示来仅针对此查询关闭它:

/*+ opt_param('_OPTIM_PEEK_USER_BINDS ',FALSE) * /

Your problem is due to bind variable peeking - turning it off for the whole database would probably break other things, but you can turn it off for just this query by adding the following hint:

/*+ opt_param('_OPTIM_PEEK_USER_BINDS ',FALSE) */

格子衫的從容 2024-07-23 17:36:52

OP告诉我们他无法更改sql语句。 通过使用dbms_advanced_rewrite包,可以拦截SQL语句并更改该SQL语句。

The OP tells us that he can't change the sql statements. With the use of package dbms_advanced_rewrite it is possible to intercept a SQL statements and to change this SQL statement.

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