我有一个参数化查询。根据参数值,最佳查询计划会有很大差异。麻烦在于:Oracle 将第一次查询调用的计划用于后续调用,导致性能不佳。我通过动态 SQL 处理它,但这种方式远非优雅。那么问题来了:有没有办法告诉Oracle必须重新计算查询计划?
7 回答
如果查询计划确实在参数值上发生了显着变化,也许你不应该为这个参数使用绑定变量。
该参数可以取多少个不同的值?如果只有几个,您最终会得到几个查询计划(每个值一个),并且这些计划有望表现良好并且可以重复使用。
或者您可以在 SQL 语句中使用注释“/* THIS IS VALUE BRACKET ONE * /”来分隔它们(或查询分析器提示,如果您觉得自己知道哪些是合适的,例如 /*+ CARDINALITY */ 可能适用这里)。
无论哪种方式,我认为您希望拥有单独的 SQL 语句,以便您可以在 Statspack 和朋友中获得单独的报告,因为看起来您真的想要微调该查询。
对于 Oracle 10g,我们会选择查询中的任何表并执行
GRANT SELECT ON table1 TO user1;
这将使引用该表的任何查询的计划无效。当然,您会希望选择一个对其他查询影响最小的表。另请参阅此页面以获取更多信息和示例列表。
如果您真的想每次都生成一个新的查询计划,只需按照 thilo 的建议添加一个独特的注释
select /* SQLID=1234 */ 1 from dual;
select /* SQLID=1235 */ 1 from dual;
这些应该产生独特的计划。
不过,我非常怀疑是否需要这样做,在尝试解决优化器之前,您应该非常确定您的统计数据没有错。
优化器使用的其中一件事是相关列上的直方图。如果您使用绑定变量并且相关列上有直方图,则计划可能会根据参数值而改变。第一个计划将保留在共享池中,并将用于所有值。
如果你不想要这个,那么你可以使用文字而不是绑定(如果你不会有太多相同的 sql 版本)。或者您可以移除直方图,移除直方图可确保独立于绑定参数值生成相同的计划。
每次执行都使 sql 无效不是一个好主意。根据这个 sql 的使用频率,它可能会导致新的问题,例如硬解析导致的闩锁问题。
有没有办法告诉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 */
您的问题是由于绑定变量窥视 - 为整个数据库关闭它可能会破坏其他东西,但您可以通过添加以下提示来关闭它仅用于此查询:
/*+ opt_param('_OPTIM_PEEK_USER_BINDS ',FALSE) */
OP 告诉我们他不能更改 sql 语句。使用包dbms_advanced_rewrite
可以截获一条 SQL 语句并更改这条 SQL 语句。