7

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

4

7 回答 7

5

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

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

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

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

于 2009-04-07T07:58:57.817 回答
4

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

GRANT SELECT ON table1 TO user1;

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

于 2009-04-07T07:47:39.327 回答
2

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

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

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

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

于 2009-04-07T08:18:10.657 回答
2

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

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

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

于 2009-04-07T13:44:16.113 回答
2

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

您可以OUTLINE为不同的执行计划创建多个 's 并选择使用哪一个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 */
于 2009-04-07T14:38:51.037 回答
0

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

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

于 2009-04-07T15:22:56.690 回答
0

OP 告诉我们他不能更改 sql 语句。使用包dbms_advanced_rewrite可以截获一条 SQL 语句并更改这条 SQL 语句。

于 2009-04-09T18:55:51.970 回答