Продолжается подписка на наши издания! Вы не забыли подписаться?

Адаптивные курсоры и управление планом выполнения SQL

К( настоящему моменту многие уже наслышаны о том, как связываемые переменные помогают в работе с SQL. Для тех, кто до сих пор о них не знает, я попробую просто разъяснить основные концепции (кроме того, рекомендую посетить сайт Тома Кайта (Tom Kyte) asktom.oracle.com, где можно убедиться, как важно использовать связываемые переменные, чтобы улучшить выполнение SQL-выражений. Там же можно узнать, как работать со связываемыми переменными на некоторых других языках.).

Допустим, у вас есть таблица под названием CUSTOMERS, в которой, помимо прочих, имеется колонка STATE_CODE, где сохраняется место жительства каждого покупателя. Как принято в США, название местности сокращено в двухбуквенные аббревиатуры: CT (Коннектикут), NY (Нью-Йорк) и т.д. Если вам, к примеру, нужно узнать, сколько покупателей совершили более трех покупок и кто из них проживает в штате Коннектикут (CT), вы, вероятнее всего, напишете следующее:

  
select count(1)
  from customers
  where state_code = 'CT'
  and times_purchased > 3;

Получив этот запрос, Oracle должен вначале произвести парсинг (разбор полученной строки), в результате чего сгенерируется план выполнения для SQL-выражения, которое вы только что передали. После разбора выражения оно готово к выполнению. Концептуально парсинг похож на компиляцию кода при разработке программного обеспечения. К примеру, когда вы пишете что-то на С++, у вас не выйдет запустить это прямо сразу в операционной системе – сначала придется скомпилировать код и сделать его исполняемым. Выполнение парсинга создает исполняемый код из SQL-выражения.

Теперь допустим, что другой пользователь передает такое выражение:

select count(1)
  from customers
  where state_code = 'NY'
  and times_purchased > 3;  

Оно почти идентично запросу, рассмотренному выше, но код штата (state_code), поиск которого здесь выполняется, – не Коннектикут (CT), а Нью-Йорк (NY). В идеальном варианте разобранный код должен бы быть одинаковым в обоих случаях, а значение букв должно подставляться во время выполнения. Но запросы написаны таким образом, что Oracle интерпретирует их как разные и поэтому разбирает второй запрос заново.

Рассмотрим, что было бы, если бы запрос записали так:

select count(1)
  from customers
  where state_code = <StateCode>
  and times_purchased > 3;

Первый запрос передал бы NY как значение параметра <StateCode>, а второй запрос – CT как значение этого же параметра, поэтому запрос не пришлось бы разбирать повторно.

В данном примере <StateCode> является «связываемой переменной», то есть местом вставки значений, которые будут подменяться во время выполнения запроса. Связываемые переменные записываются как VariableName (см. ниже):

where state_code = :state_code

Если в вашем коде нет связываемых переменных, и он вместо этого содержит кучу ссылок на строковые значения вроде where state_code = 'CT', можно принудительно заменить все строковые значения на связываемые переменные. Для этого необходимо установить параметр инициализации:

cursor_sharing = force

С помощью этого параметра выражение «where state_code = 'CT'» будет заменяться на where state_code = ":SYS_0001", где SYS_0001 – переменная, сгенерированная системой Oracle. Такой подход сделает вышеприведенные SQL-выражения идентичными.

Проблема, возникающая при использовании связываемых переменных

Если связываемые переменные так хороши, почему бы не использовать их постоянно? Разве cursor_sharing – не волшебное средство, трансформирующее весь плохой код в код с повторно используемыми выражениями? (Те, кто уже знает ответы на эти вопросы и разбирается в концепции считывания значений связываемых переменных (bind-peeking), могут сразу перейти к разделу «Адаптивные курсоры»).

Представьте, что колонка STATE_CODE является индексированной. Ниже приведено количество значений в ней:

select state_code, count(1)
from customers
group by state_code;
 
ST   COUNT(1)
-- ----------
NY     994901
CT       5099

Как видите, данные крайне ассиметричны: около 5% строк содержат 'CT', тогда как все остальные – 'NY'. Зная разницу в населенности этих штатов, удивляться не приходится. Теперь посмотрим, какой тип плана выполнения сгенерирован для запроса, рассмотренного выше.

SQL> set autot traceonly explain
SQL> select * from customers where state_code = 'NY' and times_purchased > 3
  2  /
 
Execution Plan
----------------------------------------------------------
Plan hash value: 2008213504
 
-------------------------------------------------------------------------------
| Id  | Operation         | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |           |   895K|    26M|  1532   (9)| 00:00:19 |
|*  1 |  TABLE ACCESS FULL| CUSTOMERS |   895K|    26M|  1532   (9)| 00:00:19 |
-------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter("TIMES_PURCHASED">3 AND "STATE_CODE"='NY')

Запрос использовал полный просмотр таблицы. Это вполне подходящее действие, поскольку искомое в результате оказалось в 95% строк; индексное сканирование здесь было бы накладно. Теперь рассмотрим такой же запрос со значением ‘CT’.


SQL> c/NY/CT
  1* select * from customers where state_code = 'CT' and times_purchased > 3
SQL> /
 
Execution Plan
----------------------------------------------------------
Plan hash value: 4876992
 
-------------------------------------------------------------------------------
| Id  | Operation        | Name          | Rows | Bytes | Cost(%CPU)| Time    
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT |               | 4589 |   138K|    56  (2)| 00:00:01
|*  1 |  TABLE ACCESS    | CUSTOMERS     | 4589 |   138K|    56  (2)| 00:00:01
      |  BY INDEX ROWID  |               |      |       |           |
|*  2 |   INDEX RANGE    | IN_CUST_STATE | 5099 |       |    12  (0)| 00:00:01
      |   SCAN           |               |      |       |           |
-------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter("TIMES_PURCHASED">3)
   2 - access("STATE_CODE"='CT')

Запрос использовал индексирование колонки, что вполне тут подходит. Ведь СТ занимает только 5% от общего числа строк. Так что индексное сканирование здесь очень кстати.

Посмотрим теперь, как ведет себя Oracle, если используется какая-нибудь связываемая переменная; в данном случае – в Oracle Database 10g:


SQL> var state_code varchar2(2)
SQL> exec :state_code := 'CT'
 
PL/SQL procedure successfully completed.
 
SQL> select max(times_purchased) from customers where state_code = :state_code
  2  /
 
Execution Plan

----------------------------------------------------------
Plan hash value: 296924608
 
-------------------------------------------------------------------------------
| Id  | Operation          | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |     1 |     6 |  1511   (8)| 00:00:19 
|   1 |  SORT AGGREGATE    |           |     1 |     6 |            |          
|*  2 |   TABLE ACCESS FULL| CUSTOMERS |   500K|  2929K|  1511   (8)| 00:00:19 
-------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - filter("STATE_CODE"=:STATE_CODE)

Для работы с таблицей CUSTOMERS оптимизатор выбрал полный просмотр таблицы. Но ведь мы искали только ‘СТ’, который занимает всего 5% от общего количества записей. Разве не должен был оптимизатор предпочесть индексирование? Что заставило его задействовать полный просмотр таблицы, а не индексное сканирование?

Ответ – феномен, называемый bind peeking. Вначале, когда мы запустили запрос со связываемой переменной, значение которой равнялось ‘NY’, оптимизатору пришлось произвести длительный разбор. В процессе парсинга он обратился к нашей связываемой переменной, чтобы узнать, какое у нее значение. Значение это было 'NY, и ему соответствовало почти 95% строк, в связи с чем, как и ожидалось, оптимизатор выбрал полный просмотр таблицы. В качестве дополнительного действия оптимизатор «заморозил» план выполнения для нашего запроса. Затем, когда мы запустили такой же запрос, но со значением ‘CT’, оптимизатор использовал этот же план, без пересчитывания, хотя он и не являлся оптимальным способом получения результата. Используй мы в запросе значение типа ‘CT’ (литерную переменную) вместо связываемой переменной, оптимизатор был бы в состоянии подобрать более подходящий план выполнения.

Таким образом, хотя использование связываемых переменных в большинстве случаев довольно удобно, эти переменные неэффективны там, где селективность значений существенно влияет на планы. Именно это произошло в нашем примере: селективность значений 'CT' и 'NY равнялась соответственно 5 и 95% строк. В случаях, когда распределение данных таково, что селективность почти одинакова для всех значений, план выполнения так же останется одинаковым. Поэтому программисты, опытные в SQL, знают, когда можно нарушить основное правило использования связываемых переменных и применить вместо них прямые строковые константы.

Адаптивные курсоры

Но что же делать, если у вас нет опытных программистов или не хватает времени на переписывание этих выражений? Есть ли в Oracle какие-нибудь толковые альтернативы?

Да, есть. В Oracle Database 11g у курсоров появилась сообразительность. Вместо того, чтобы каждый раз при выполнении запроса тупо использовать закешированный план выполнения, они решают, быть ли плану пересчитанным, когда изменяется значение связываемой переменной. Если в курсоре присутствует связываемая переменная, то база данных некоторое время за ней наблюдает, чтобы определить, какие значения передаются в эту переменную, и нуждается ли план выполнения в пересчете. В случае, когда пересчет плана необходим, курсор маркируется как «чувствительный к связыванию» (Bind-Sensitive).

Пример запроса, который мы тут рассмотрели, в этом смысле идеален. Корректный план оптимизатора будет зависеть от значения связываемой переменной. Вам ничего не нужно делать – все произойдет автоматически.

Представление V$SQL модифицировано добавлением двух колонок: IS_BIND_SENSITIVE и IS_BIND_AWARE. Посмотрим теперь, как они используются.

select is_bind_sensitive, is_bind_aware, sql_id, child_number from v$sql
  where sql_text = 'select count(1) from customers 
    where state_code = :state_code 
    and times_purchased > 3'

I I SQL_ID        CHILD_NUMBER
- - ------------- ------------
Y Y 7cv5271zx2ttg            0
Y N 7cv5271zx2ttg            1

Вначале узнаем, что эти колонки означают. Oracle некоторое время наблюдает за курсорами и видит, как различаются значения. Если разные значения потенциально могут изменить план выполнения, курсор помечается как Bind-Sensitive, и колонка IS_BIND_SENSITIVE показывает "Y". После нескольких выполнений база данных знает о курсорах и значениях уже больше, и решает, заставить ли курсор изменить основанный на этих значениях план. В случае положительного решения курсор маркируется как Bind-Aware, и колонка IS_BIND_AWARE показывает "Y". Вывод: Bind-Sensitive курсоры лишь потенциально могут изменять планы выполнения, в то время как Bind-Aware курсоры делают это в обязательном порядке.

Новое представление V$SQL_CS_HISTOGRAM показывает, сколько раз было выполнено SQL-выражение. Оно разделено на три столбца: по одному на каждый курсор-потомок (см. ниже).

select * from v$sql_cs_histogram
where sql_id = '7cv5271zx2ttg'
/
 
ADDRESS  HASH_VALUE SQL_ID        CHILD_NUMBER  BUCKET_ID      COUNT
-------- ---------- ------------- ------------ ---------- ----------
45C8218C 2144429871 7cv5271zx2ttg            5          0          0
45C8218C 2144429871 7cv5271zx2ttg            5          1          2
45C8218C 2144429871 7cv5271zx2ttg            5          2          0
45C8218C 2144429871 7cv5271zx2ttg            4          0          8
... and so on ...
45C8218C 2144429871 7cv5271zx2ttg            0          2          0

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

select * from v$sql_cs_selectivity
where sql_id = '7cv5271zx2ttg'
/
 
ADDRESS  HASH_VALUE SQL_ID        CHILD_NUMBE PREDICATE   R LOW      HIGH
-------- ---------- ------------- ----------- ----------- - -------- ----------
45C8218C 2144429871 7cv5271zx2ttg           5 =STATE_CODE 0 0.895410   1.094391
45C8218C 2144429871 7cv5271zx2ttg           4 =STATE_CODE 0 0.004589   0.005609
45C8218C 2144429871 7cv5271zx2ttg           4 =STATE_CODE 1 0.002295   0.002804
45C8218C 2144429871 7cv5271zx2ttg           3 =STATE_CODE 0 0.002295   0.002804
45C8218C 2144429871 7cv5271zx2ttg           0 =STATE_CODE 0 0.004589   0.005609

Это представление предоставляет массу информации. Колонка PREDICATE содержит различные предикаты (условия WHERE), которые применялись пользователем. Значения LOW и HIGH показывают диапазон величин для переданных значений.

И, наконец, третье новое представление, V$SQL_CS_STATISTICS, показывает активность курсоров, которые маркированы как Bind-Aware или Bind-Sensitive.

select child_number, 
      bind_set_hash_value, 
      peeked, 
      executions, 
      rows_processed, 
      buffer_gets, 
      cpu_time
  from v$sql_cs_statistics
  where sql_id = '7cv5271zx2ttg';
 
CHILD_NUM BIND_SET_HASH_VAL P EXECUTIONS ROWS_PROCESSED BUFFER_GETS   CPU_TIME
------------ ------------------- - ---------- ---------------------- ----------
        1          22981142 Y          1           9592        3219          0
        0          22981142 Y          1           9592        3281          0

Это представление отображает статистику выполнения, как она записана базой данных. Колонка EXECUTIONS показывает, сколько раз выполнялся запрос с разными значениями в связываемой переменной. В колонке PEEKED (выглядит как «P») выведено, обращался ли оптимизатор к значению связываемой переменной, чтобы подобрать оптимальный план выполнения.

Все эти представления дают дополнительную информацию, не являющуюся необходимой для понимания принципов работы данной функциональности. Адаптивные курсоры активируются и используются автоматически.

Управление планом выполнения SQL

Сколько раз вы наблюдали такую ситуацию: запрос обладает идеальным планом, но его обязательно что-нибудь нарушит? Этим «чем-нибудь» может являться кто-то, проанализировавший таблицу заново, или же изменение существенного параметра (например, star-transformer), оказавшее влияние на оптимизатор, - список возможностей бесконечен. Отчаявшись, вы можете запретить любые изменения в базе данных: никаких сборов статистики, никаких изменений в параметрах, и т.п.

Но это легче сказать, чем сделать. Что происходит, когда изменяется паттерн данных? Возьмем, к примеру, случай, рассмотренный в разделе «Адаптивные курсоры». Таблица CUSTOMERS теперь заполнена покупателями из Нью-Йорка, так что the STATE_CODE, в основном, "NY". И когда выполняетя запрос с предикатом

where state_code = 'NY'

индексного сканирования не происходит. Вместо этого система выполняет полный просмотр таблицы. Если же предикат такой:

where state_code = 'CT'

то индексирование используется, потому что возвращено будет всего несколько строк. Но что же случится, если изменится паттерн? Скажем, покупателей из Коннектикута (state_code = 'CT') окажется так много, что количество CT подскочит до 70%? В этом случае запрос для CT должен использовать полный просмотр таблицы. Но поскольку сбор статистики для оптимизатора вами запрещен, оптимизатор не узнает об изменении в паттерне и продолжит сканировать с использованием индекса, а это тут малопродуктивно. Что же можно сделать?

А что, если бы Oracle использовал оптимальный план, но пересмотрел бы его, как только обуславливающие факторы (например, сбор статистики или параметры базы данных) изменились, и применил бы новый план – но только – и только! – в том случае, если новый план был бы лучше? Это было бы здорово, правда? Так вот, в Oracle Databse 11g такое возможно. Давайте посмотрим, как.

SQL Plan Baselining

Когда ранее определенный оптимизатором план нуждается в обновлении из-за каких-нибудь изменений в основных факторах, это обновление не происходит в Oracle Database 11g немедленно. Вначале Oracle лишь оценивает новый план, а задействует его только в том случае, если он улучшает план, который уже применяется. Вдобавок существуют утилиты и интерфейсы, с помощью которых можно посмотреть историю планирования для каждого запроса, а также узнать, как эти планы соотносятся друг с другом.

Жизненный цикл начинается с того, что Oracle определяет выражение, которое выполнено более одного раза, т.е. является «повторяемым». Как только повторяемое выражение выявлено, его план запоминается и сохраняется в базе данных как SQL Plan Baseline, в логической конструкции, именуемой SQL Management Base (SMB). SMB сохраняет каждый план для запроса: как он был сгенерирован, и т.д.

Планы, однако, не сохраняются в SMB автоматически. Если бы это было так, то в SMB хранились бы планы каждого типа запросов, и она стала бы необъятной. Напротив, вы можете и должны контролировать, сколько запросов попадет в SMB. Существует два способа это сделать: включить автоматическое сохранение в SMB всех повторяемых запросов или же вручную загружать запросы, которые должны включаться в SMB.

Давайте сначала рассмотрим простой случай. Вы можете сделать так, чтобы SQL Plan Management автоматически записывал бы планы выполнения (SQL Plan Baselines) для всех повторяемых запросов. Для этого нужно установить значение TRUE в параметр базы данных optimizer_capture_sql_plan_baselines (по умолчанию этот параметр равен FALSE). По счастью, это динамический параметр.

SQL> alter system optimizer_capture_sql_plan_baselines = true;

После выполнения данного выражения планы выполнения для всех повторяемых выражений сохраняются в SMB в виде SQL Plan Baselines. SQL Plan Baselines сохраняются в представлении, которое называется DBA_SQL_PLAN_BASELINES. Их также можно увидеть в утилите Enterprise Manager. Для этого следует запустить Enterprise Manager и выбрать закладку «Server». На этой странице нужно выбрать «SQL Plan Control» в разделе «Query Optimizer». Откроется главная страница SPM. Далее выбирается закладка «SQL Plan Baseline». Это и есть главный экран SQL Plan Baseline. В левом верхнем углу находятся конфигурационные параметры. Там показано значение TRUE для параметра Capture SQL Plan Baselines (записывать планы SQL автоматически), которое было включено командой ALTER SYSTEM. Под ним указан параметр Use SQL Plan Baselines, установленный равным TRUE (значение по умолчанию). Этот параметр сообщает, что SQL Plan Baselines будут использованы для запроса, когда таковой появится.

Всякий раз, когда для запроса генерируется новый план, старый план сохраняется в истории в SMB. Но это ведь значит, что SMB будет переполнена историями планов. Параметр, контролирующий количество недель, в течение которых планы хранятся в истории, указан в текстовом поле напротив надписи «Хранение планов (недели)» (Plan Retention (Weeks)). Экран показывает, что установлено значение «53 недели». Это значит, что, если SQL Plan Baseline не будет использован в течение 53 недель, он удалится автоматически.

Посередине экрана находится поле для поиска SQL-выражений. Введите искомую строку, нажмите «Go», и вы увидите SQL-выражения, а также ассоциированные с ними планы, как было показано на рисунке выше. Каждый план содержит обширную информацию о своем состоянии. Давайте проанализируем эти состояния.

Такая же информация, и даже больше, находится и в представлении DBA_SQL_PLAN_BASELINES:

SQL> desc DBA_SQL_PLAN_BASELINES
 Name                                      Null?    Type
 ----------------------------------------- -------- ---------------
 SIGNATURE                                 NOT NULL NUMBER
 SQL_HANDLE                                NOT NULL VARCHAR2(30)
 SQL_TEXT                                  NOT NULL CLOB
 PLAN_NAME                                 NOT NULL VARCHAR2(30)
 CREATOR                                            VARCHAR2(30)
 ORIGIN                                             VARCHAR2(14)
 PARSING_SCHEMA_NAME                                VARCHAR2(30)
 DESCRIPTION                                        VARCHAR2(500)
 VERSION                                            VARCHAR2(64)
 CREATED                                   NOT NULL TIMESTAMP(6)
 LAST_MODIFIED                                      TIMESTAMP(6)
 LAST_EXECUTED                                      TIMESTAMP(6)
 LAST_VERIFIED                                      TIMESTAMP(6)
 ENABLED                                            VARCHAR2(3)
 ACCEPTED                                           VARCHAR2(3)
 FIXED                                              VARCHAR2(3)
 AUTOPURGE                                          VARCHAR2(3)
 OPTIMIZER_COST                                     NUMBER
 MODULE                                             VARCHAR2(48)
 ACTION                                             VARCHAR2(32)
 EXECUTIONS                                         NUMBER
 ELAPSED_TIME                                       NUMBER
 CPU_TIME                                           NUMBER
 BUFFER_GETS                                        NUMBER
 DISK_READS                                         NUMBER
 DIRECT_WRITES                                      NUMBER
 ROWS_PROCESSED                                     NUMBER
 FETCHES                                            NUMBER
 END_OF_FETCH_COUNT                                 NUMBER

Нажав на имя плана, можно увидеть детализацию это плана, как на приведенном ниже рисунке.

В детализацию включается план выполнения запроса вкупе с информацией о том, является ли план принятым (accepted), включенным (enabled), фиксированным (fixed), и другими важными сведениями. Еще один значимый атрибут – «Origin» (источник), который здесь показывает значение AUTO-CAPTURE. Это означает, что план был принят системой автоматически, потому что параметр optimizer_capture_sql_plan_baselines установлен равным TRUE.

Чтобы вернуться к списку планов, который был изображен на предыдущей иллюстрации, выберите «Возврат» (Return). Теперь выберите план, статус которого не «принят», и нажмите на «Evolve» (Раскрыть), чтобы увидеть, следует ли рассматривать данный план как кандидата в потенциально лучшие планы.

Важной деталью на появляющемся в результате этого экране является радиокнопка «Verify Performance» (Проверка производительности). Ее выбирают, если нужно проверить разные планы и сравнить их производительность с планом выполнения SQL, который используется для конкретного запроса. Нажмите OK, и вы увидите отчет о сравнении:

-------------------------------------------------------------------------------
                        Evolve SQL Plan Baseline Report
-------------------------------------------------------------------------------

Inputs:
-------
  PLAN_LIST  = SYS_SQL_PLAN_b5429522ee05ab0e
               SYS_SQL_PLAN_b5429522e53beeec
  TIME_LIMIT = DBMS_SPM.AUTO_LIMIT
  VERIFY     = YES
  COMMIT     = YES

Plan: SYS_SQL_PLAN_b5429522e53beeec
-----------------------------------
  It is already an accepted plan.

Plan: SYS_SQL_PLAN_b5429522ee05ab0e
-----------------------------------
  Plan was verified: Time used 3.9 seconds.
  Failed performance criterion: Compound improvement ratio <= 1.4.

                      Baseline Plan      Test Plan     Improv. Ratio
                      -------------      ---------     -------------
  Execution Status:        COMPLETE       COMPLETE
  Rows Processed:                 1              1
  Elapsed Time(ms):            3396            440              7.72
  CPU Time(ms):                1990            408              4.88
  Buffer Gets:                 7048           5140              1.37
  Disk Reads:                  4732             53             89.28
  Direct Writes:                  0              0
  Fetches:                     4732             25            189.28
  Executions:                     1              1

Это вполне показательный отчет, демонстрирующий соотношение планов между собой. Если видно, что какой-нибудь из них выполняется лучше, оптимизатор использует именно его. Если же новый план не дает заметного улучшения при выполнении запросов, его не следует принимать и использовать. Управление планом выполнения SQL (SQL Performance Management) позволяет видеть, как сравниваются планы, и задействовать те из них, что дают действительно лучший результат.

Существует возможность изменить статус «принят» плана вручную. Для этого следует выполнить пакет DBMS_SPM:

declare
   ctr binary_integer;
begin
   ctr := dbms_spm.alter_sql_plan_baseline (
      sql_handle      => 'SYS_SQL_e0b19f65b5429522',
      plan_name       => 'SYS_SQL_PLAN_b5429522ee05ab0e',
      attribute_name  => 'ACCEPTED',
      attribute_value => 'NO'
   );
end;

Можно отключить SQL Plan Baseline так, чтобы он не использовался оптимизатором. Можно так же включить его заново, и оптимизатор опять будет его использовать. Выключается план следующим образом:

declare
   ctr binary_integer;
begin
   ctr := dbms_spm.alter_sql_plan_baseline (
      sql_handle      => 'SYS_SQL_e0b19f65b5429522',
      plan_name       => 'SYS_SQL_PLAN_b5429522ee05ab0e',
      attribute_name  => 'ENABLED',
      attribute_value => 'NO'
   );
end;

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

Отличия от Stored Outlines

<...>

Заключение

Адаптивные курсоры и SQL Plan Management – всего лишь два примера того, как база данных теперь сама разбирается в запросах, и как она на них реагирует. Такое ее поведение позволяет получить лучший результат: в работе с адаптивными курсорами можно использовать связываемые переменные, не теряя при этом в оптимальности плана выполнения, а применение SQL Plan Management дает не намертво фиксированный план выполнения, а предоставляет возможность улучшать его в течение какого-то времени, сохраняя при этом краткосрочную его стабильность.


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

Copyright © 1994-2016 ООО "К-Пресс"