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

时间:2009-04-07 07:40:28

标签: oracle sql-execution-plan

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

7 个答案:

答案 0 :(得分:4)

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

该参数可以使用多少个不同的值?如果只有少数几个,那么最终会得到一些查询计划(每个值一个),这些计划可能会很好地运行并且可以重复使用。

或者你可以在SQL语句中使用注释“/ *这是VALUE BRACKET ONE * /”来分隔它们(或者查询分析器提示,如果你想知道哪些是合适的,比如/ * + CARDINALITY * /可能适用于此处。)

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

答案 1 :(得分:2)

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

GRANT SELECT ON table1 TO user1;

这会使引用此表的任何查询的计划无效。当然,您可能希望选择对其他查询影响最小的表。有关详细信息和示例列表,另请参阅this page

答案 2 :(得分:2)

如果您真的想每次都生成一个新的查询计划,只需在thilo建议中添加一个唯一的评论

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

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

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

答案 3 :(得分:2)

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

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

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

答案 4 :(得分:2)

  

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

答案 5 :(得分:0)

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

/ * + opt_param('_ OPTIM_PEEK_USER_BINDS',FALSE)* /

答案 6 :(得分:0)

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