Есть ли способ заставить Oracle пересчитать план запроса для каждого вызова запроса? - PullRequest
4 голосов
/ 07 апреля 2009

У меня есть параметризованный запрос. В зависимости от значений параметров оптимальный план запроса значительно варьируется. Вот в чем проблема: Oracle использует план из первого вызова запроса для последующих вызовов, что приводит к снижению производительности. Я имею дело с динамическим SQL, но этот путь далеко не элегантен. Таким образом, вопрос: есть ли способ сообщить Oracle, что план запроса должен быть пересчитан?

Ответы [ 7 ]

4 голосов
/ 07 апреля 2009

Если план запроса действительно существенно изменяется для значения параметра, возможно, вам не следует использовать переменные связывания для этого параметра.

Сколько разных значений может принимать этот параметр? Если их всего несколько, у вас получится пара планов запросов (по одному для каждого значения), которые, надеюсь, будут работать хорошо и могут быть использованы повторно.

Или вы можете использовать комментарии "/ * ЭТО ЗНАЧИТЕЛЬНЫЙ БРАКЕТ ОДИН * /" в выражении SQL для их разделения (или подсказки анализатора запросов, если вы чувствуете, что знаете, какие из них подходят, что-то вроде / * + CARDINALITY * / может применяться здесь).

В любом случае, я думаю, что вы хотите иметь отдельные операторы SQL, чтобы вы могли получать отдельные отчеты в Statspack и друзьях, потому что похоже, что вы действительно хотите точно настроить этот запрос.

2 голосов
/ 07 апреля 2009

Есть ли способ сообщить 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 */
2 голосов
/ 07 апреля 2009

Одна из вещей, которую использует оптимизатор, - это гистограммы на соответствующих столбцах. Если вы используете переменную связывания и у вас есть гистограммы в связанном столбце, план может измениться в зависимости от значения параметра. Этот первый план останется в общем пуле и будет использоваться для всех значений.

Если вы не хотите этого, вы можете использовать литералы вместо связывания (если у вас не будет слишком много версий одного и того же sql). Или вы можете удалить гистограмму, удаление гистограммы гарантирует, что независимо от значения параметра привязки будет сгенерирован один и тот же план.

Отмена SQL для каждого выполнения не очень хорошая идея. В зависимости от того, как часто этот sql используется, он может вызвать новые проблемы, такие как проблемы с защелками, вызванные жестким анализом.

2 голосов
/ 07 апреля 2009

Если вы действительно хотите каждый раз генерировать новый план запроса, просто добавьте уникальный комментарий, как подсказывает тило

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

Это должно генерировать уникальные планы.

Я бы очень подозрительно относился к необходимости сделать это, хотя, прежде чем пытаться обойти оптимизатор, вы должны быть уверены, что ваша статистика не ошибается.

2 голосов
/ 07 апреля 2009

Для Oracle 10g мы бы выбрали любую таблицу в запросе и выполнили бы

GRANT SELECT ON table1 TO user1;

Это приведет к аннулированию плана любого запроса, ссылающегося на эту таблицу. Конечно, вы хотели бы выбрать таблицу, которая оказывает минимальное влияние на другие запросы. См. Также эту страницу для получения дополнительной информации и примера списка.

0 голосов
/ 09 апреля 2009

ОП сообщает нам, что он не может изменить операторы sql. С использованием пакета dbms_advanced_rewrite можно перехватывать операторы SQL и изменять этот оператор SQL.

0 голосов
/ 07 апреля 2009

Ваша проблема связана с просмотром переменной привязки - ее отключение для всей базы данных, вероятно, сломает другие вещи, но вы можете отключить его только для этого запроса, добавив следующую подсказку:

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

...