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

Posted

技术标签:

【中文标题】有没有办法让 Oracle 为每个查询调用重新计算查询计划?【英文标题】:Is there a way to make Oracle recalculate a query plan for each query invocation? 【发布时间】:2009-04-07 07:40:27 【问题描述】:

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

【问题讨论】:

【参考方案1】:

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

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

或者您可以在 SQL 语句中使用 cmets "/* THIS IS VALUE BRACKET ONE * /" 来分隔它们(或查询分析器提示,如果您觉得自己知道哪些是合适的,例如 /*+ CARDINALITY * / 可能适用于此)。

无论哪种方式,我认为您都希望拥有单独的 SQL 语句,以便您可以在 Statspack 和朋友中获得单独的报告,因为看起来您确实想要微调该查询。

【讨论】:

【参考方案2】:

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

GRANT SELECT ON table1 TO user1;

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

【讨论】:

【参考方案3】:

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

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

这些应该会产生独特的计划。

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

【讨论】:

我无法修改 SQL 源。统计数据很好。就我而言,问题是查询计划已过时且不好。如果我使计划无效,Oracle 将计算出新的好计划。 如果您根本无法更改 SQL 源,那么 Il-Bhima 是您的最佳选择。【参考方案4】:

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

如果你不想这样,那么你可以使用文字而不是绑定(如果你不会有太多相同的 sql 版本)。或者您可以移除直方图,移除直方图可确保独立于绑定参数值生成相同的计划。

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

【讨论】:

【参考方案5】:

有没有办法告诉 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 */

【讨论】:

【参考方案6】:

您的问题是由于绑定变量偷看 - 为整个数据库关闭它可能会破坏其他东西,但您可以通过添加以下提示来关闭它:

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

【讨论】:

他可能想要相反的意思,“每次都偷看”。我想知道是否也有提示。 “每次偷看”就是使用文字。【参考方案7】:

OP 告诉我们他不能更改 sql 语句。使用包dbms_advanced_rewrite 可以拦截SQL 语句并更改该SQL 语句。

【讨论】:

以上是关于有没有办法让 Oracle 为每个查询调用重新计算查询计划?的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 2 + JdbcTemplate - 有没有办法为每个支持的数据库提供 SQL 查询?

Python:有没有办法让 swipl 查询产生值 true 而不是 ?

在 Oracle 的 select 语句中使用对象类型

有没有办法使用合并语句将此查询转换为 Oracle 查询?

有没办法在postgreSQL中查询oracle上的数据

停止中继:查询渲染器为某些 setStates 重新加载数据