Руководство по настройке SQL. Примечание 20. Повышение реальной производительности за счет совместного использования курсора.

Эта статья представляет собой примечание к главе 20 «Повышение реальной производительности за счет совместного использования курсоров» Руководства по настройке SQL .

Совместное использование курсора может на порядки повысить производительность приложения базы данных.

важные базовые понятия

  • Цель оптимизатора
    Определение приоритетов использования ресурсов оптимизатором. Используя параметр инициализации OPTIMIZER_MODE, вы можете установить цель оптимизатора: наилучшая пропускная способность или наилучшее время отклика.

  • переменная связывания
    Заполнитель в операторе SQL, который должен быть заменен допустимым значением или адресом значения для успешного выполнения оператора. Используя переменные связывания, вы можете написать оператор SQL, который принимает входные данные или параметры во время выполнения.

20.1 Обзор совместного использования курсора

База данных Oracle может совместно использовать курсоры, которые являются указателями на частные области SQL в общем пуле.

20.1.1 О курсорах

Частная область SQL содержит информацию о проанализированных операторах SQL и другую информацию, относящуюся к сеансу, для обработки.

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

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

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

20.1.1.1 Частные и общие области SQL

Курсоры в частных областях SQL указывают на общие области SQL в библиотечном кэше .

В отличие от частных областей SQL, которые содержат информацию о состоянии сеанса, общие области SQL содержат дерево синтаксического анализа оператора и план выполнения . Например, SELECT * FROM employeesвыполнение имеет план и дерево синтаксического анализа, хранящиеся в общей области SQL. В качестве другого примера, SELECT * FROM employeesпри выполнении команды , которая отличается как синтаксисом, так и семантикой, план и дерево синтаксического анализа хранятся в отдельной общей области SQL.

Несколько частных областей SQL в одном или разных сеансах могут ссылаться на одну общую область SQL — явление, известное как совместное использование курсора . Например, выполнение в одном сеансе SELECT * FROM employeesи выполнение в другом сеансе SELECT * FROM employees(доступ к одной и той же таблице) могут использовать одно и то же дерево синтаксического анализа и план. Общая область SQL, к которой обращаются несколько операторов, называется общим курсором.
вставьте сюда описание изображения
База данных Oracle использует следующие шаги, чтобы автоматически определить, является ли выданный оператор SQL или блок PL/SQL текстовым идентичным другому оператору, находящемуся в настоящее время в библиотечном кэше:

  1. Вычисляет хеш-значение текста инструкции .
  2. База данных ищет соответствующие хэши для существующих операторов SQL в общем пуле. Возможны следующие варианты:
    – Нет подходящего хеш-значения.
    В этом случае в разделяемом пуле в настоящее время нет инструкции SQL, поэтому база данных выполняет жесткий синтаксический анализ . Это завершит проверку общего пула.
    – Есть соответствующий хеш.
    В этом случае база данных переходит к следующему шагу — сопоставлению текста.
  3. База данных сравнивает текст оператора сопоставления с текстом оператора хеширования, чтобы определить, совпадают ли они. Возможны следующие варианты:
    – Ошибка сопоставления текста.
    В этом случае процесс сопоставления текста останавливается, что приводит к жесткому синтаксическому анализу.
    – Текст соответствует успешно.
    В этом случае база данных переходит к следующему шагу: определяет, может ли SQL совместно использовать существующий родительский курсор.
    Для совпадения текста текст инструкции SQL или блока PL/SQL должен быть идентичен посимвольно , включая пробелы, заглавные буквы и комментарии. Например, следующие операторы не могут использовать одну и ту же общую область SQL:
    SELECT * FROM employees;
    SELECT * FROM Employees;
    SELECT *  FROM employees;
    
    Как правило, операторы SQL, отличающиеся только литералами (в данном примере 121 и 247), не могут использовать одну и ту же общую область SQL. Например, следующие операторы не анализируются в одной и той же области SQL:
    SELECT count(1) FROM employees WHERE manager_id = 121;
    SELECT count(1) FROM employees WHERE manager_id = 247;
    
    Единственным исключением из этого правила является случай, когда для параметра CURSOR_SHARING установлено значение FORCE, и в этом случае аналогичные операторы могут совместно использовать области SQL.

20.1.1.2 Родительский и дочерний курсоры

Каждая проанализированная инструкция SQL имеет родительский курсор и один или несколько дочерних курсоров.

Родительский курсор хранит текст оператора SQL. Два оператора используют один и тот же родительский курсор, если их текст одинаков. Однако если текст отличается, база данных создает отдельный родительский курсор.

В этом примере первые два утверждения синтаксически различны (буква «с» в первом утверждении строчная, а во втором — заглавная), но семантически идентичны. Эти операторы имеют разные родительские курсоры из-за различий в синтаксисе. Третий оператор синтаксически идентичен первому оператору (строчная буква «с»), но отличается семантически, поскольку ссылается на таблицу клиентов в другой схеме. Поскольку синтаксис тот же, третья инструкция может иметь общий родительский курсор с первой инструкцией.

SQL> CONNECT oe@inst1
Enter password: *******
Connected.
SQL> SELECT COUNT(*) FROM customers;

  COUNT(*)
----------
       319

SQL> SELECT COUNT(*) FROM Customers;

  COUNT(*)
----------
       319

SQL> CONNECT sh@inst1
Enter password:  *******
Connected.
SQL> SELECT COUNT(*) FROM customers;

  COUNT(*)
----------
    55500

Следующий запрос V$SQL указывает на два родительских узла. Оператор с идентификатором SQL 8h916vv2yw400, версия инструкции в нижнем регистре "c", имеет родительский курсор и два дочерних курсора: дочерний 0 и дочерний 1. Оператор с идентификатором SQL 5rn2uxjtpz0wd, версия оператора с заглавной буквой «c», имеет другой родительский курсор и только один дочерний курсор: дочерний 0.

SQL> CONNECT SYSTEM@inst1
Enter password:  *******
Connected.

COL SQL_TEXT FORMAT a30
COL CHILD# FORMAT 99999
COL EXEC FORMAT 9999
COL SCHEMA FORMAT a6
SELECT SQL_ID, PARSING_SCHEMA_NAME AS SCHEMA, SQL_TEXT, 
CHILD_NUMBER AS CHILD#, EXECUTIONS AS EXEC FROM V$SQL 
WHERE SQL_TEXT LIKE '%ustom%' AND SQL_TEXT NOT LIKE '%SQL_TEXT%' ORDER BY SQL_ID;

SQL_ID        SCHEMA SQL_TEXT                       CHILD#  EXEC
------------- ------ ------------------------------ ------ -----
5rn2uxjtpz0wd OE     SELECT COUNT(*) FROM Customers      0     1
8h916vv2yw400 OE     SELECT COUNT(*) FROM customers      0     1
8h916vv2yw400 SH     SELECT COUNT(*) FROM customers      1     1
20.1.1.2.1 Родительские курсоры и V$SQLAREA

Представление V$SQLAREA содержит по одной строке для каждого родительского курсора.

В приведенном ниже примере запрос V$SQLAREA показывает два родительских курсора, каждый из которых идентифицируется с другим SQL_ID . VERSION_COUNT указывает количество дочерних курсоров .
В исходном тексте этого SQL есть ошибка

COL SQL_TEXT FORMAT a30
SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE
FROM   V$SQLAREA
WHERE  SQL_TEXT LIKE '%ustomer%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';
 
SQL_TEXT                       SQL_ID        VERSION_COUNT HASH_VALUE
------------------------------ ------------- ------------- ----------
SELECT COUNT(*) FROM customers 8h916vv2yw400             2 3320713216
SELECT COUNT(*) FROM Customers 5rn2uxjtpz0wd             1 1935639437

В предыдущем выводе значение SELECT * FROM customersVERSION_COUNT, равное 2, указывает на несколько дочерних курсоров, что необходимо, поскольку оператор был выполнен для двух разных объектов. Напротив, оператор SELECT * FROM Customers (обратите внимание на заглавную «E») выполняется только один раз, поэтому есть родительский курсор и дочерний курсор (VERSION_COUNT равен 1).

20.1.1.2.2 Дочерние курсоры и V$SQL

Каждый родительский курсор имеет один или несколько дочерних курсоров.

Подкурсор содержит план выполнения, переменные связывания, метаданные об объектах, на которые ссылается запрос, среду оптимизатора и другую информацию. В отличие от родительских курсоров, дочерние курсоры не сохраняют текст оператора SQL .

Если оператор может повторно использовать родительский курсор, база данных проверяет, может ли оператор повторно использовать существующий дочерний курсор. База данных выполняет несколько проверок, в том числе следующие:

  • База данных сравнивает объекты, указанные в выданном операторе, с объектами, на которые ссылаются операторы в пуле, чтобы убедиться, что все они одинаковы.
    Ссылки на объекты схемы в операторе SQL или блоке PL/SQL должны разрешаться в один и тот же объект в той же схеме. Например, если два пользователя выполняют следующую инструкцию SQL, и у каждого пользователя есть своя собственная таблица сотрудников, следующая инструкция не будет идентичной, поскольку она ссылается на разные таблицы сотрудников для каждого пользователя:
SELECT * FROM employees;

Примечание. Базы данных могут совместно использовать курсоры в частных временных таблицах, но только в рамках одного сеанса. База данных связывает идентификатор сеанса как часть контекста курсора. Во время мягкого разрешения база данных может совместно использовать дочерние курсоры только в том случае, если идентификатор текущего сеанса совпадает с идентификатором сеанса в контексте курсора.

  • База данных определяет, совпадают ли режимы оптимизатора.
    Например, операторы SQL должны быть оптимизированы с использованием одной и той же цели оптимизатора.

Пример 20-2. Несколько подкурсоров
V$SQL описывает операторы, находящиеся в настоящее время в библиотечном кэше. Он содержит одну строку для каждого дочернего курсора, как показано в следующем примере:

COL SQL_TEXT FORMAT A30
COL USR FORMAT A6
SELECT SQL_TEXT, SQL_ID, USERNAME AS USR, CHILD_NUMBER AS CHILD#, 
       HASH_VALUE, PLAN_HASH_VALUE AS PLAN_HASHV
FROM   V$SQL s, DBA_USERS d
WHERE  SQL_TEXT LIKE '%ustomer%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%'
AND    d.USER_ID = s.PARSING_USER_ID;

SQL_TEXT                       SQL_ID        USR        CHILD# HASH_VALUE PLAN_HASHV
------------------------------ ------------- ------ ---------- ---------- ----------
SELECT COUNT(*) FROM customers 8h916vv2yw400 OE              0 3320713216 1140397121
SELECT COUNT(*) FROM Customers 5rn2uxjtpz0wd OE              0 1935639437 1140397121
SELECT COUNT(*) FROM customers 8h916vv2yw400 SH              1 3320713216  237477902
20.1.1.2.3 Несоответствия курсоров и V$SQL_SHARED_CURSOR

Если родительский курсор имеет несколько дочерних курсоров, представление V$SQL_SHARED_CURSOR предоставляет информацию о том, почему курсоры не являются общими. Для некоторых типов несовместимости столбец TRANSLATION_MISMATCH указывает на несоответствие со значением Y или N.

Пример 20-3 Несоответствие перевода
В этом примере столбец TRANSLATION_MISMATCH показывает, что два оператора (SELECT * FROM сотрудников) ссылаются на разные объекты, в результате чего для последнего оператора TRANSLATION_MISMATCH имеет значение Y, равное Y. Поскольку он не может использоваться совместно, у каждого оператора есть отдельный дочерний курсор, на что указывают 0 и 1 для CHILD_NUMBER.

SELECT S.SQL_TEXT, S.CHILD_NUMBER, s.CHILD_ADDRESS, 
       C.TRANSLATION_MISMATCH
FROM   V$SQL S, V$SQL_SHARED_CURSOR C
WHERE  SQL_TEXT LIKE '%ustomer%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%'
AND S.CHILD_ADDRESS = C.CHILD_ADDRESS;

SQL_TEXT                       CHILD_NUMBER CHILD_ADDRESS    T
------------------------------ ------------ ---------------- -
SELECT COUNT(*) FROM customers            0 00000000A7208390 N
SELECT COUNT(*) FROM customers            1 00000000A7192130 Y
SELECT COUNT(*) FROM Customers            0 00000000A71F3EF8 N

20.1.2 О курсорах и синтаксическом анализе

Если приложение выдает оператор, а база данных Oracle не может повторно использовать курсор, оно должно построить новую исполняемую версию кода приложения. Эта операция называется жестким разбором .

Мягкий синтаксический анализ — это любой синтаксический анализ, который не является жестким синтаксическим анализом и происходит, когда база данных может повторно использовать существующий код . Некоторые программные синтаксические анализы требуют меньше ресурсов, чем другие. Например, Oracle Database может выполнять различные оптимизации и сохранять дочерние курсоры в общей области SQL, если родительский курсор оператора уже существует. Однако база данных Oracle также должна хранить родительский курсор в общей области SQL, если родительский курсор не существует, что требует дополнительных затрат памяти.

По сути, жесткий синтаксический анализ перекомпилирует оператор перед его запуском. Жесткий разбор операторов SQL перед каждым выполнением аналогичен перекомпиляции программы на C перед каждым выполнением . Жесткий разбор выполняет следующие операции :

  • Проверьте синтаксис операторов SQL
  • Проверьте семантику операторов SQL
  • Проверить права доступа пользователя, выдавшего выписку
  • Создайте план выполнения
  • Несколько раз обращайтесь к кешу библиотеки и кешу словаря данных, чтобы проверить словарь данных.

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

По всем вышеперечисленным причинам накладные расходы ЦП и памяти на жесткий синтаксический анализ могут вызвать серьезные проблемы с производительностью. Эти проблемы особенно очевидны в веб-приложениях, которые принимают пользовательский ввод из форм, а затем динамически генерируют операторы SQL. Real World Performance Group настоятельно рекомендует свести к минимуму жесткий синтаксический анализ, насколько это возможно .

https://youtu.be/WcRcmfSIajc

20.1.3 О литералах и переменных связывания

Переменные связывания имеют решающее значение для совместного использования курсора в приложениях Oracle Database.

20.1.3.1 Литералы и курсоры

Некоторые приложения Oracle используют литералы вместо переменных связывания при построении операторов SQL.

Например, оператор SELECT SUM(salary) FROM hr.employees WHERE employee_id < 101 использует буквальное значение 101 в качестве идентификатора сотрудника. По умолчанию Oracle Database не может использовать совместное использование курсора, если аналогичные операторы не используют переменные связывания. Таким образом, Oracle Database обрабатывает идентичный оператор, за исключением значения 102 или любого другого случайного значения, как совершенно новый оператор, который необходимо тщательно проанализировать.

Real-World Performance Group определила приложения, использующие текст, как распространенную причину проблем с производительностью, масштабируемостью и безопасностью . В реальном мире не редкость быстро писать приложения, не принимая во внимание совместное использование курсора. Типичным примером является приложение для очистки экрана, которое копирует содержимое из веб-формы, а затем объединяет строки для динамического построения операторов SQL.

К основным проблемам, вызванным использованием литеральных значений, относятся:

  • Приложения, которые объединяют текст, введенный конечными пользователями, уязвимы для атак путем внедрения SQL. Эту угрозу можно устранить, только переписав приложение для использования переменных связывания.
  • Если каждый оператор подвергается жесткому анализу, курсоры не используются совместно, поэтому базе данных приходится потреблять больше памяти для создания курсоров.
  • База данных Oracle должна блокировать общий пул и библиотечный кэш во время жесткого разрешения. По мере увеличения количества жестких синтаксических анализов увеличивается и количество процессов, ожидающих блокировки общего пула. Эта ситуация уменьшает параллелизм и увеличивает конкуренцию.

https://youtu.be/EBdZ-RE2HFs

Пример 20-6 Совместное использование текста и курсора

Рассмотрим приложение, выполняющее следующие операторы, которые отличаются только словами:

SELECT SUM(salary) FROM hr.employees WHERE employee_id < 101;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 120;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 165;

Приведенный ниже запрос V$SQLAREA показывает, что для трех операторов требуются три разных родительских курсора. Для каждого родительского курсора требуется собственный дочерний курсор, на что указывает VERSION_COUNT.

COL SQL_TEXT FORMAT a30
SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE
FROM   V$SQLAREA
WHERE  SQL_TEXT LIKE '%mployee%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';

SQL_TEXT                              SQL_ID VERSION_COUNT HASH_VALUE
------------------------------ ------------- ------------- ----------
SELECT SUM(salary) FROM hr.emp b1tvfcc5qnczb             1  191509483
loyees WHERE employee_id < 165
SELECT SUM(salary) FROM hr.emp cn5250y0nqpym             1 2169198547
loyees WHERE employee_id < 101
SELECT SUM(salary) FROM hr.emp au8nag2vnfw67             1 3074912455
loyees WHERE employee_id < 120

20.1.3.2 Связывание переменных и курсоров

Вы можете разрабатывать приложения Oracle для использования переменных связывания вместо литералов.

Переменные связывания — это заполнители в запросах. Например, оператор SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id использует переменную связывания: emp_id в качестве идентификатора сотрудника.

Команда Real-World Performance обнаружила, что приложения, использующие переменные связывания, работают лучше, лучше масштабируются и являются более безопасными. К основным преимуществам использования переменных связывания относятся:

  • Приложения, использующие переменные связывания, не уязвимы для тех же атак с внедрением кода SQL, что и приложения, использующие литералы.
  • База данных Oracle может использовать преимущества совместного использования курсора, когда один и тот же оператор использует переменные связывания, и совместно использовать планы и другую информацию, когда разные значения привязаны к одному и тому же оператору.
  • Oracle Database позволяет избежать накладных расходов на блокировку общего пула и библиотечного кэша, необходимых для жесткого синтаксического анализа.

Пример 20-7. Связывание переменных и общих курсоров

В следующем примере создается переменная связывания emp_id с помощью команды VARIABLE в SQL*Plus, затем выполняется запрос с тремя разными значениями связывания (101, 120 и 165):

VARIABLE emp_id NUMBER

EXEC :emp_id := 101;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;
EXEC :emp_id := 120;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;
EXEC :emp_id := 165;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;

Следующий запрос V$SQLAREA показывает уникальную инструкцию SQL:

COL SQL_TEXT FORMAT a34
SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE
FROM   V$SQLAREA
WHERE  SQL_TEXT LIKE '%mployee%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';

SQL_TEXT                           SQL_ID        VERSION_COUNT HASH_VALUE
---------------------------------- ------------- ------------- ----------
SELECT SUM(salary) FROM hr.employe 4318cbskba8yh             1 615850960
es WHERE employee_id < :emp_id

Значение VERSION_COUNT, равное 1, указывает, что база данных повторно использует один и тот же дочерний курсор вместо создания трех отдельных дочерних курсоров. Это повторное использование стало возможным благодаря использованию переменных связывания .

20.1.3.3 Просмотр переменных привязки

При поиске переменной связывания (также известном как поиск связывания) оптимизатор просматривает значение в переменной связывания, когда база данных выполняет жесткий анализ оператора.

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

Когда в запросе используются литералы, оптимизатор может использовать литеральные значения для поиска наилучшего плана. Однако когда в запросе используются переменные связывания, оптимизатор должен выбрать наилучший план при отсутствии литералов в тексте SQL. Эта задача может быть очень сложной. Просматривая связанные значения во время начального жесткого синтаксического анализа, оптимизатор может определить кардинальность условий предложения WHERE, как если бы использовались литералы, тем самым улучшая планы.

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

Пример 20-8 Литералы приводят к различным планам выполнения

Предположим, вы выполняете следующие операторы, которые выполняют три разных оператора с разными литералами (101, 120 и 165), а затем отображаете план выполнения для каждого оператора:

SET LINESIZE 167
SET PAGESIZE 0
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 101;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 120;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 165;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());

База данных жестко проанализировала все три оператора, которые не были идентичными. Вывод DISPLAY_CURSOR (отредактированный для ясности) показывает, что оптимизатор выбрал один и тот же план сканирования диапазона индексов для первых двух операторов, но план сканирования полной таблицы для оператора с использованием литерала 165:

SQL_ID	cn5250y0nqpym, child number 0
-------------------------------------
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 101

Plan hash value: 2410354593

-------------------------------------------------------------------------------------
|Id| Operation                            | Name         |Rows|Bytes|Cost(%CPU)|Time|
-------------------------------------------------------------------------------------
| 0| SELECT STATEMENT                     |               |  |   |2 (100)|          |
| 1|  SORT AGGREGATE                      |               |1 | 8 |       |          |
| 2|   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES     |1 | 8 |2 (0)  | 00:00:01 |
|*3|    INDEX RANGE SCAN                  | EMP_EMP_ID_PK |1 |   |1 (0)  | 00:00:01 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMPLOYEE_ID"<101)

SQL_ID	au8nag2vnfw67, child number 0
-------------------------------------
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 120

Plan hash value: 2410354593

-------------------------------------------------------------------------------------
|Id| Operation                            | Name         |Rows|Bytes|Cost(%CPU)|Time|
-------------------------------------------------------------------------------------
| 0| SELECT STATEMENT                     |               |  |   |2 (100)|          |
| 1|  SORT AGGREGATE                      |               |1 | 8 |       |          |
| 2|   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES     |20|160|2 (0)  | 00:00:01 |
|*3|    INDEX RANGE SCAN                  | EMP_EMP_ID_PK |20|   |1 (0)  | 00:00:01 |
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMPLOYEE_ID"<120)

SQL_ID	b1tvfcc5qnczb, child number 0
-------------------------------------
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 165

Plan hash value: 1756381138

-------------------------------------------------------------------------
| Id  | Operation          | Name      |Rows| Bytes |Cost(%CPU)| Time   |
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |    |       | 2 (100)|          |
|   1 |  SORT AGGREGATE    |           |  1 |     8 |        |          |
|*  2 |   TABLE ACCESS FULL| EMPLOYEES | 66 |   528 | 2   (0)| 00:00:01 |
-------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("EMPLOYEE_ID"<165)

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

Пример 20-9. Связывание переменных вызывает повторное использование курсора

В этом примере запрос, выполненный в примере 20.8, переписывается для использования переменных связывания вместо литералов. Вы привязываете одинаковые значения (101, 120 и 165) к переменной связывания: emp_id, а затем отображаете план выполнения для каждого значения:

VAR emp_id NUMBER

EXEC :emp_id := 101;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
EXEC :emp_id := 120;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
EXEC :emp_id := 165;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());

Вывод DISPLAY_CURSOR показывает, что оптимизатор выбрал один и тот же план для всех трех операторов:

SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id

Plan hash value: 2410354593

-------------------------------------------------------------------------------------
| Id | Operation                            | Name      |Rows|Bytes|Cost (%CPU)|Time|
-------------------------------------------------------------------------------------
|  0 | SELECT STATEMENT                     |               | |  |2 (100)|          |
|  1 |  SORT AGGREGATE                      |               |1|8 |       |          |
|  2 |   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES     |1|8 |  2 (0)| 00:00:01 |
|* 3 |    INDEX RANGE SCAN                  | EMP_EMP_ID_PK |1|  |  1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMPLOYEE_ID"<:EMP_ID)

Напротив, когда приведенный выше оператор выполняется буквально, оптимизатор выбирает менее затратное полное сканирование таблицы, когда значение идентификатора сотрудника равно 165. Эту проблему решает адаптивное совместное использование курсора.

20.1.4 О жизненном цикле общих курсоров

Когда оптимизатор анализирует новый оператор SQL, отличный от DDL, база данных выделяет новую общую область SQL. Требуемый объем памяти зависит от сложности оператора.

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

Общие области SQL могут стать недействительными из-за изменений в зависимых объектах схемы или статистике оптимизатора. Oracle Database использует два метода управления жизненным циклом курсора: недействительность и недействительность прокрутки.

20.1.4.1 Курсор помечен как недействительный

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

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

База данных немедленно помечает зависимую общую область SQL как недействительную при соблюдении следующих условий:

  • Если параметр NO_INVALIDATE имеет значение FALSE, DBMS_STATS собирает статистику для таблиц, кластеров таблиц или индексов.
  • Операторы SQL относятся к объектам схемы, которые впоследствии изменяются операторами DDL с использованием немедленного аннулирования курсора (по умолчанию).
    Вы можете вручную указать немедленную недействительность таких операторов, как ALTER TABLE ... IMMEDIATE VALIDATION и ALTER INDEX ... IMMEDIATE VALIDATION , или установить для параметра инициализации CURSOR_INVALIDATION значение IMMEDIATE на уровне сеанса или системы.
    Примечание. Операторы DDL, использующие предложение DEFERRED VALIDATION, переопределяют установку IMMEDIATE параметра инициализации CURSOR_INVALIDATION.

Когда вышеуказанные условия соблюдены, база данных повторно проанализирует затронутый оператор при следующем его выполнении.

Когда база данных делает курсор недействительным, значение VSQL.INVALIDATIONS увеличивается (например, с 0 до 1), значение VSQL.INVALIDATIONS увеличивается (например, с 0 до 1), а значение VSQL.INVALIDATIONS увеличивается (для например, от 0 до 1), и VЗначение SQ L.IN V A L I D A T I ONS увеличивается (например, от 0 до 1 ) , а V SQL.OBJECT_STATUS показывает INVALID_UNAUTH .

Пример 20-10 Принудительно делает курсор недействительным, устанавливая NO_INVALIDATE=FALSE.

SQL> SELECT COUNT(*) FROM sales;

  COUNT(*)
----------
    918843

SQL> SELECT PREV_SQL_ID, SQL_ID FROM V$SESSION WHERE SID = SYS_CONTEXT('userenv', 'SID');

SQL_ID
-------------
1y17j786c7jbh

SQL> SELECT CHILD_NUMBER, EXECUTIONS,
  2  PARSE_CALLS, INVALIDATIONS, OBJECT_STATUS 
  3  FROM V$SQL WHERE SQL_ID = '1y17j786c7jbh';

CHILD_NUMBER EXECUTIONS PARSE_CALLS INVALIDATIONS OBJECT_STATUS
------------ ---------- ----------- ------------- -------------
           0          1           1             0 VALID

SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS(null,'sales',no_invalidate => FALSE);

PL/SQL procedure successfully completed.

SQL> SELECT CHILD_NUMBER, EXECUTIONS,
  2  PARSE_CALLS, INVALIDATIONS, OBJECT_STATUS
  3  FROM V$SQL WHERE SQL_ID = '1y17j786c7jbh';

CHILD_NUMBER EXECUTIONS PARSE_CALLS INVALIDATIONS OBJECT_STATUS
------------ ---------- ----------- ------------- --------------
           0          1           1             1 INVALID_UNAUTH

20.1.4.2 Курсоры, помеченные как недействительные

Когда курсор помечен как прокручивающийся недействительный (V$SQL.IS_ROLLING_INVALID имеет значение Y), база данных будет постепенно выполнять жесткий синтаксический анализ в течение длительного периода времени.

Примечание. Когда V$SQL.IS_ROLLING_REFRESH_INVALID имеет значение Y, базовый объект изменился, но курсор не нужно перекомпилировать. База данных обновляет метаданные в курсоре.

Цель прокрутки аннулирования

Так как резкое увеличение числа жестких синтаксических анализов может значительно снизить производительность, инвалидация прокрутки (также известная как отложенная инвалидация) полезна для рабочих нагрузок, которые делают недействительными несколько курсоров одновременно. База данных назначает каждому недопустимому курсору случайно сгенерированный период времени. Области SQL, срок действия которых истекает в одно и то же время, обычно имеют разные периоды времени.

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

Примечание. Если параллельный оператор SQL помечен как скользящий недействительный, то база данных выполнит жесткий анализ при следующем выполнении, независимо от того, истекло ли время. В среде Oracle Real Application Clusters (Oracle RAC) эта технология обеспечивает согласованность между планами выполнения сервера параллельного выполнения и координатора запросов.

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

Спецификация задержанного отказа

По умолчанию DDL указывает, что инструкции, которые обращаются к объектам, используют немедленную недействительность курсора. Например, если вы создаете таблицу или индекс, курсоры, ссылающиеся на эту таблицу или индекс, будут использовать немедленную недействительность.

Если оператор DDL поддерживает отложенную недействительность курсора, поведение по умолчанию можно переопределить с помощью таких операторов, как ALTER TABLE ... DEFERRED INVALIDATION. Параметры зависят от оператора DDL. Например, ALTER INDEX поддерживает только DEFERRED INVALIDATION, когда также указаны параметры UNUSABLE или REBUILD.

Альтернативой DDL является установка для параметра инициализации CURSOR_INVALIDATION значения DEFERRED на уровне сеанса или системы. Операторы DDL, использующие предложение IMMEDIATE INVALIDATION, переопределяют значение DEFERRED параметра инициализации CURSOR_INVALIDATION.

когда происходит сбой прокрутки

Если атрибут DEFERRED INVALIDATION применяется к объекту либо в результате настроек DDL, либо параметров инициализации, операторы, обращающиеся к этому объекту, могут подвергаться отложенной аннулированию. База данных помечает общую область SQL как скользящую недействительную в любом из следующих случаев:

  • Если для параметра NO_INVALIDATE установлено значение DBMS_STATS.AUTO_INVALIDATE, DBMS_STATS собирает статистику для таблиц, кластеров таблиц или индексов. Это значение по умолчанию.

  • Не предотвращая использование отложенной недействительности, выполните одно из следующих утверждений с отложенной недействительностью:

    • ALTER TABLE для секционированных таблиц
    • ИЗМЕНИТЬ ТАБЛИЦУ… ПАРАЛЛЕЛЬНО
    • ИЗМЕНИТЬ ИНДЕКС… НЕПОДХОДЯЩИЙ
    • ИЗМЕНИТЬ ИНДЕКС… ВОССТАНОВИТЬ
    • СОЗДАТЬ ИНДЕКС
    • ИНДЕКС ПАДЕНИЯ
    • TRUNCATE TABLE для секционированных таблиц

    Подмножество инструкций DDL требуют немедленного аннулирования курсоров для DML (INSERT, UPDATE, DELETE или MERGE), а инструкции SELECT — нет. Ряд факторов, связанных с конкретным оператором DDL и затронутыми курсорами, определяет, использует ли база данных Oracle отложенную недействительность.

20.2 CURSOR_SHARING и подстановка переменных привязки

В этом разделе объясняется, что такое параметр инициализации CURSOR_SHARING и как установка для него разных значений влияет на то, как Oracle Database использует переменные связывания.

20.2.1 Параметр инициализации CURSOR_SHARING

Параметр инициализации CURSOR_SHARING управляет тем, как база данных обрабатывает операторы с переменными связывания.

В Oracle Database 12c этот параметр поддерживает следующие значения:

  • EXACT
    Это значение по умолчанию. База данных позволяет использовать только операторы с одинаковым текстом для совместного использования курсора. База данных не будет пытаться заменить литеральные значения сгенерированными системой переменными связывания. В этом случае оптимизатор создает план для каждого оператора на основе буквального значения.

  • База данных FORCE заменяет все литералы сгенерированными системой переменными связывания . Для идентичных операторов после связывания литералов подстановки переменных оптимизатор использует один и тот же план.

ПРИМЕЧАНИЕ. Значение SIMILAR для CURSOR_SHARING устарело.

Вы можете установить CURSOR_SHARING на уровне системы или сеанса или использовать подсказку CURSOR_SHARING_EXACT на уровне оператора.

20.2.2 Поведение при синтаксическом анализе, когда CURSOR_SHARING = FORCE

Когда оператор SQL использует литералы вместо переменных связывания, установка для параметра инициализации CURSOR_SHARING значения FORCE позволяет базе данных заменить литералы переменными связывания, сгенерированными системой . Используя этот метод, база данных иногда может уменьшить количество родительских курсоров в общей области SQL.

Примечание. Если в операторе используется предложение ORDER BY, курсор не обязательно является разделяемым, хотя база данных может выполнять литеральную замену в этом предложении, поскольку разные значения номеров столбцов в качестве литералов означают разные результаты запроса и могут иметь разные планы выполнения. Номера столбцов в предложении ORDER BY влияют на планирование и выполнение запросов, поэтому база данных не может совместно использовать два курсора с разными номерами столбцов .

Когда для CURSOR_SHARING установлено значение FORCE, база данных выполняет следующие шаги во время синтаксического анализа:

  1. Скопируйте все литералы в операторе в PGA и замените их сгенерированными системой переменными связывания.
-- 替换前
SELECT SUBSTR(last_name, 1, 4), SUM(salary) 
FROM   hr.employees 
WHERE  employee_id < 101 GROUP BY last_name
-- 替换后
SELECT SUBSTR(last_name, :"SYS_B_0", :"SYS_B_1"), SUM(salary) 
FROM   hr.employees 
WHERE  employee_id < :"SYS_B_2" GROUP BY last_name
  1. Поиск в общем пуле той же инструкции (то же значение хэш-кода SQL).
    Если такая же инструкция не найдена, база данных выполняет жесткий анализ. В противном случае база данных переходит к следующему шагу.
  2. Мягкий разбор операторов выполнения

Как показано на предыдущих шагах, установка для параметра инициализации CURSOR_SHARING значения FORCE не уменьшает количество разрешений. И наоборот, FORCE позволяет базе данных выполнять мягкий синтаксический анализ вместо жесткого синтаксического анализа при определенных обстоятельствах. Кроме того, FORCE не защищает от атак SQL-инъекций, поскольку Oracle Database привязывает значения после того, как произошла любая инъекция.

Пример 20-11 Замена литералов системными переменными связывания
В этом примере для CURSOR_SHARING устанавливается значение FORCE на уровне сеанса, выполняются три инструкции, содержащие литералы, и отображается план для каждой инструкции:

ALTER SESSION SET CURSOR_SHARING=FORCE;
SET LINESIZE 170
SET PAGESIZE 0
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 101;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 120;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 165;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());

Следующий вывод DISPLAY_CURSOR, отредактированный для удобочитаемости, показывает, что все три инструкции используют один и тот же план. Оптимизатор выбрал план сканирования диапазона индексов, потому что он рассмотрел первое значение (101), связанное с системной переменной связывания, и выбрал этот план как лучший план для всех значений. На самом деле, этот план не является лучшим планом для всех ценностей. При значении 165 полное сканирование таблицы более эффективно.

SQL_ID	cxx8n1cxr9khn, child number 0
-------------------------------------
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :"SYS_B_0"

Plan hash value: 2410354593

-------------------------------------------------------------------------------------
| Id  | Operation                           | Name       |Rows|Bytes|Cost(%CPU)|Time|
-------------------------------------------------------------------------------------
|  0 | SELECT STATEMENT                     |               |  |   |2 (100)|        |
|  1 |  SORT AGGREGATE                      |               |1 | 8 |       |        |
|  2 |   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES     |1 | 8 |2 (0)  |00:00:01|
|* 3 |    INDEX RANGE SCAN                  | EMP_EMP_ID_PK |1 |   |1 (0)  |00:00:01|
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMPLOYEE_ID"<101)

Запрос к V$SQLAREA подтверждает, что Oracle Database заменила текст системной переменной связывания: "SYS_B_0" и создала родительский и дочерний курсоры (VERSION_COUNT=1) для всех трех операторов, что означает, что все выполнения используют один и тот же план. из .

COL SQL_TEXT FORMAT a36
SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE
FROM   V$SQLAREA
WHERE  SQL_TEXT LIKE '%mployee%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';

SQL_TEXT                             SQL_ID        VERSION_COUNT HASH_VALUE
------------------------------------ ------------- ------------- ----------
SELECT SUM(salary) FROM hr.employees cxx8n1cxr9khn             1  997509652
 WHERE employee_id < :"SYS_B_0"

20.3 Адаптивный общий доступ к курсору

Адаптивное совместное использование курсора позволяет использовать несколько планов выполнения для одного оператора, содержащего переменные связывания .

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

20.3.1 Цель адаптивного совместного использования курсора

При просмотре с привязкой оптимизатор просматривает значение определяемой пользователем переменной связывания при первом вызове курсора.

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

При адаптивном совместном использовании курсора база данных отслеживает доступ к данным с течением времени для различных связанных значений, гарантируя, что лучший курсор выбирается для определенного связанного значения. Например, оптимизатор может выбрать один план для граничного значения 10 и другой план для граничного значения 50. Совместное использование курсора является «адаптивным» в том смысле, что курсор регулирует свое поведение, поэтому оптимизатор не всегда выбирает одни и те же значения переменных плана для каждого выполнения или привязки. Таким образом, оптимизатор автоматически определяет, когда различные выполнения инструкции могут быть выгодны при использовании разных планов выполнения.

Примечание. Адаптивное совместное использование курсора не зависит от параметра инициализации CURSOR_SHARING . Адаптивное совместное использование курсора в равной степени применяется к операторам, содержащим определяемые пользователем и сгенерированные системой переменные связывания. Адаптивное совместное использование курсора не применяется к операторам, содержащим только литералы.

20.3.2 Как работает адаптивное совместное использование курсора: пример

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

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

SELECT * FROM employees WHERE salary = :sal AND department_id = :dept

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

  1. В первый раз приложение выдает оператор, что приводит к жесткому синтаксическому анализу. Во время синтаксического анализа база данных выполняет следующие задачи:
    • Просмотрите переменные связывания, чтобы создать первоначальный план.
    • Помечает курсор как чувствительный к привязке. Курсор, чувствительный к привязке, — это курсор, оптимальный план которого может зависеть от значения переменной привязки. Чтобы определить, выгодны ли разные планы, база данных отслеживает поведение чувствительных к привязке курсоров, использующих разные значения привязки.
    • Сохраните метаданные о предикате, включая мощность связанного значения (в этом примере предполагается, что возвращаются только 5 строк).
    • Создайте план выполнения (в данном случае доступ к индексу) на основе просмотренных значений.
  2. База данных выполняет курсор, сохраняя связанные значения и статистику выполнения в курсоре.
  3. Приложение выдает оператор во второй раз, используя другую переменную связывания, в результате чего база данных выполняет программный анализ и находит соответствующий курсор в библиотечном кэше.
  4. База данных реализует курсоры.
  5. База данных выполняет следующие задачи после выполнения:
    • База данных сравнивает статистику выполнения второго выполнения со статистикой первого выполнения.
    • База данных наблюдает за всеми ранее выполненными статистическими шаблонами, прежде чем решить, следует ли пометить курсор как поддерживающий привязку. В этом примере предположим, что база данных решает, что курсор поддерживает привязку.
  6. Приложение выдает оператор в третий раз, используя другую переменную связывания, что вызывает программный анализ. Поскольку курсоры поддерживают привязку, база данных делает следующее:
    • Определяет, находится ли основание нового значения в том же диапазоне, что и сохраненное основание. В этом примере кардинальность аналогична: 8 строк вместо 5.
    • Повторно используйте план выполнения в существующем дочернем курсоре.
  7. База данных реализует курсоры.
  8. Приложение выдает оператор в четвертый раз, используя другую переменную связывания, что приводит к мягкому синтаксическому анализу. Поскольку курсоры поддерживают привязку, база данных делает следующее:
    • Определяет, находится ли основание нового значения в том же диапазоне, что и сохраненное основание. В этом примере количество элементов совсем другое: 102 строки (в таблице со 107 строками) вместо 5 строк.
    • Соответствующий дочерний курсор не найден.
  9. База данных выполняет жесткий синтаксический анализ. В результате база данных делает следующее:
    • Создайте новый дочерний курсор, используя второй план выполнения (в данном случае полное сканирование таблицы)
    • Хранить метаданные о предикате в курсоре, включая мощность связанных значений.
  10. База данных выполняет новый курсор.
  11. База данных сохраняет новые связанные значения и статистику выполнения в новом дочернем курсоре.
  12. Приложение выдает оператор в пятый раз, используя другую переменную связывания, что вызывает программный анализ. Поскольку курсоры поддерживают привязку, база данных делает следующее:
    • Определяет, находится ли основание нового значения в том же диапазоне, что и сохраненное основание. В этом примере основание равно 20.
    • Соответствующий дочерний курсор не найден.
  13. База данных выполняет жесткий синтаксический анализ. В результате база данных делает следующее:
    • Создайте новый дочерний курсор, используя третий план выполнения (в данном случае доступ к индексу)
    • Убедитесь, что этот план выполнения доступа к индексу совпадает с планом выполнения доступа к индексу, который использовался при первом выполнении оператора.
    • Объединяет два подкурсора, содержащие планы доступа к индексу, что включает в себя сохранение объединенной кардинальной статистики в один подкурсор и удаление другого.
  14. База данных обращается к плану выполнения, используя индекс для выполнения курсора.

20.3.3 Чувствительные к привязке курсоры

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

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

Оптимизатор использует следующие критерии, чтобы решить, является ли курсор чувствительным к привязке:

  • Оптимизатор просмотрел связанные значения, чтобы сгенерировать оценки кардинальности.
  • Связывание используется для предикатов равенства или диапазона.

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

Пример 20-12 Столбцы со значительным перекосом данных
В этом примере предполагается, что столбец hr.employees.department_id имеет значительный перекос данных. SYSTEM выполняет следующий код настройки, чтобы добавить 100 000 сотрудников отдела 50 в таблицу сотрудников в схеме примера, всего 100 107 строк, а затем собирает статистику таблицы:

DELETE FROM hr.employees WHERE employee_id > 999;

ALTER TABLE hr.employees DISABLE NOVALIDATE CONSTRAINT emp_email_uk;

DECLARE
v_counter NUMBER(7) := 1000;
BEGIN
 FOR i IN 1..100000 LOOP    
 INSERT INTO hr.employees 
   VALUES (v_counter, null, 'Doe', '[email protected]', null,'07-JUN-02',
     'AC_ACCOUNT', null, null, null, 50);
 v_counter := v_counter + 1;
 END LOOP;
END;
/
COMMIT; 

BEGIN
  DBMS_STATS.GATHER_TABLE_STATS (ownname = 'hr',tabname => 'employees');
END;
/

ALTER SYSTEM FLUSH SHARED_POOL;

Следующий запрос отображает гистограмму для столбца employee.department_id:

COL TABLE_NAME FORMAT a15
COL COLUMN_NAME FORMAT a20
COL HISTOGRAM FORMAT a9

SELECT TABLE_NAME, COLUMN_NAME, HISTOGRAM
FROM   DBA_TAB_COLS
WHERE  OWNER = 'HR'
AND    TABLE_NAME = 'EMPLOYEES'
AND    COLUMN_NAME = 'DEPARTMENT_ID';

TABLE_NAME      COLUMN_NAME          HISTOGRAM
--------------- -------------------- ---------
EMPLOYEES       DEPARTMENT_ID        FREQUENCY

Пример 20-13. Запрос с низкой кардинальностью

Этот пример продолжает пример из примера 20.12. Следующий запрос показывает, что значение 10 имеет чрезвычайно низкую кардинальность для столбца Department_id, занимая 0,00099% строк:

VARIABLE dept_id NUMBER
EXEC :dept_id := 10;
SELECT COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id;

  COUNT(*) MAX(EMPLOYEE_ID)
---------- ----------------
         1              200

Оптимизатор выбирает сканирование диапазона индексов, как и ожидается для таких запросов с низкой кардинальностью:

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR);

PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID	a9upgaqqj7bn5, child number 0
-------------------------------------
select COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id

Plan hash value: 1642965905

-------------------------------------------------------------------------------------
| Id| Operation                           | Name       |Rows|Bytes|Cost (%CPU)|Time |
-------------------------------------------------------------------------------------
| 0| SELECT STATEMENT                     |                   |  |  |2(100)|        |
| 1|  SORT AGGREGATE                      |                   |1 |8 |      |        |
| 2|   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES         |1 |8 |2  (0)|00:00:01|
|*3|    INDEX RANGE SCAN                  | EMP_DEPARTMENT_IX |1 |  |1  (0)|00:00:01|
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("DEPARTMENT_ID"=:DEPT_ID)

Следующий запрос V$SQL получает информацию о курсорах:

COL BIND_AWARE FORMAT a10
COL SQL_TEXT FORMAT a22
COL CHILD# FORMAT 99999
COL EXEC FORMAT 9999
COL BUFF_GETS FORMAT 999999999
COL BIND_SENS FORMAT a9
COL SHARABLE FORMAT a9

SELECT SQL_TEXT, CHILD_NUMBER AS CHILD#, EXECUTIONS AS EXEC, 
       BUFFER_GETS AS BUFF_GETS, IS_BIND_SENSITIVE AS BIND_SENS, 
       IS_BIND_AWARE AS BIND_AWARE, IS_SHAREABLE AS SHARABLE
FROM   V$SQL
WHERE  SQL_TEXT LIKE '%mployee%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';

SQL_TEXT               CHILD#  EXEC  BUFF_GETS BIND_SENS BIND_AWARE SHARABLE
---------------------- ------ ----- ---------- --------- ---------- --------
SELECT COUNT(*), MAX(e      0     1        196         Y          N        Y
mployee_id) FROM hr.em
ployees WHERE departme
nt_id = :dept_id

Предыдущие выходные данные показывают подкурсор, выполненный один раз для запроса с низкой кардинальностью. Курсор помечен как чувствительный к привязке, поскольку оптимизатор считает, что наилучший план может зависеть от значения переменной привязки.

Когда курсор помечен как чувствительный к привязке, Oracle Database отслеживает поведение курсора с разными значениями привязки, чтобы определить, являются ли более эффективными разные планы для разных значений привязки. База данных помечает этот курсор как чувствительный к привязке, поскольку оптимизатор использует гистограмму в столбце Department_id для расчета селективности предиката WHERE Department_id = :dept_id. Поскольку наличие гистограммы указывает на то, что столбец перекошен, для разных значений переменных связывания могут потребоваться разные планы.

Пример 20-14. Запрос с высокой кардинальностью

Этот пример продолжает пример из примера 20-13. Следующий код повторно выполняет тот же запрос со значением 50, которое занимает 99,9% строк:

EXEC :dept_id := 50;
SELECT COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id;

  COUNT(*) MAX(EMPLOYEE_ID)
---------- ----------------
    100045	     100999

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

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR);

PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID	a9upgaqqj7bn5, child number 0
-------------------------------------
SELECT COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id

Plan hash value: 1642965905

-------------------------------------------------------------------------------------
| Id| Operation                           | Name       |Rows|Bytes|Cost (%CPU)|Time |
-------------------------------------------------------------------------------------
| 0| SELECT STATEMENT                     |                   |  |  |2(100)|        |
| 1|  SORT AGGREGATE                      |                   |1 |8 |      |        |
| 2|   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES         |1 |8 |2  (0)|00:00:01|
|*3|    INDEX RANGE SCAN                  | EMP_DEPARTMENT_IX |1 |  |1  (0)|00:00:01|
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("DEPARTMENT_ID"=:DEPT_ID)

Запрос V$SQL показывает, что подчиненный курсор был выполнен дважды:

SELECT SQL_TEXT, CHILD_NUMBER AS CHILD#, EXECUTIONS AS EXEC, 
       BUFFER_GETS AS BUFF_GETS, IS_BIND_SENSITIVE AS BIND_SENS, 
       IS_BIND_AWARE AS BIND_AWARE, IS_SHAREABLE AS SHARABLE
FROM   V$SQL
WHERE  SQL_TEXT LIKE '%mployee%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';

SQL_TEXT               CHILD#  EXEC  BUFF_GETS BIND_SENS BIND_AWARE SHARABLE
---------------------- ------ ----- ---------- --------- ---------- --------
SELECT COUNT(*), MAX(e      0     2       1329         Y          N        Y
mployee_id) FROM hr.em
ployees WHERE departme
nt_id = :dept_id

20.3.4 Курсоры с поддержкой привязки

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

После того, как курсор станет осведомленным о привязке, оптимизатор выбирает план будущих исполнений на основе связанных значений и оценок их кардинальности. Таким образом, «осведомленность о привязке» по сути означает «наилучший план для текущих связанных значений».

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

Если база данных помечает курсор как поддерживающий привязку, при следующем выполнении курсора база данных сделает следующее:

  • Создать новый план на основе связанных значений
  • Необработанный курсор, сгенерированный для инструкции, помечается как недоступный для совместного использования (V$SQL.IS_SHAREABLE имеет значение N). Исходный курсор больше недоступен и может быть устаревшим из библиотечного кеша.

Когда один и тот же запрос выполняется повторно с разными значениями привязки, база данных добавляет и классифицирует новые значения привязки в «сигнатуре» оператора SQL (включая среду оптимизатора, настройки NLS и т. д.). База данных проверяет связанное значение и решает, приводит ли текущее связанное значение к существенному изменению объема данных или достаточно ли существующего плана. Базе данных не нужно создавать новый план для каждого нового значения.

Рассмотрим ситуацию, когда вы выполняете оператор с 12 разными связанными значениями (по два для каждого значения), что приводит к тому, что база данных запускает 5 жестких синтаксических анализов и создает 2 дополнительных плана. Поскольку база данных выполнила 5 жестких синтаксических анализов, она создала 5 новых дочерних курсоров, даже несмотря на то, что у некоторых курсоров был тот же план выполнения, что и у существующих курсоров. База данных помечает избыточные курсоры как непригодные для использования, что означает, что эти курсоры со временем устареют из библиотечного кэша.

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

Пример 20-15. Курсоры с поддержкой привязки
Этот пример является продолжением примера из раздела «Курсоры с поддержкой привязки». Следующий код выдает второй запрос сотрудников с переменной связывания, установленной на 50:

EXEC :dept_id := 50;
SELECT COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id;

  COUNT(*) MAX(EMPLOYEE_ID)
---------- ----------------
    100045           100999

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

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR);

PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID	a9upgaqqj7bn5, child number 1
-------------------------------------
SELECT COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id

Plan hash value: 1756381138

---------------------------------------------------------------------------
| Id  | Operation         | Name      | Rows  | Bytes | Cost(%CPU)| Time  |
---------------------------------------------------------------------------
|  0 | SELECT STATEMENT   |           |       |      |254 (100)|          |
|  1 |  SORT AGGREGATE    |           |     1 |    8 |         |          |
|* 2 |   TABLE ACCESS FULL| EMPLOYEES |  100K | 781K |254  (15)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("DEPARTMENT_ID"=:DEPT_ID)

Следующий запрос V$SQL получает информацию о курсорах:

SELECT SQL_TEXT, CHILD_NUMBER AS CHILD#, EXECUTIONS AS EXEC, 
       BUFFER_GETS AS BUFF_GETS, IS_BIND_SENSITIVE AS BIND_SENS, 
       IS_BIND_AWARE AS BIND_AWARE, IS_SHAREABLE AS SHAREABLE
FROM   V$SQL
WHERE  SQL_TEXT LIKE '%mployee%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';

SQL_TEXT               CHILD#  EXEC  BUFF_GETS BIND_SENS BIND_AWARE SHAREABLE
---------------------- ------ ----- ---------- --------- ---------- ---------
SELECT COUNT(*), MAX(e      0     2       1329         Y          N         N
mployee_id) FROM hr.em
ployees WHERE departme
nt_id = :dept_id

SELECT COUNT(*), MAX(e      1     1        800         Y          Y         Y
mployee_id) FROM hr.em
ployees WHERE departme
nt_id = :dept_id

Предыдущий вывод показывает, что база данных создала дополнительный дочерний курсор (CHILD# из 1). Курсор 0 теперь помечен как недоступный для совместного использования. Курсор 1 показывает много буферов ниже курсора 0 и помечен как чувствительный к связыванию и поддерживающий связывание. Курсоры, поддерживающие привязку, могут использовать разные планы для разных значений привязки в зависимости от избирательности предикатов, содержащих переменные привязки.

Пример 20-16 Binding-Aware Cursor: выбор наилучшего плана

Этот пример продолжает пример в «Примере 20-15». Следующий код выполняет тот же запрос сотрудников со значением 10 и очень низкой кардинальностью (только одна строка):

EXEC :dept_id := 10;
SELECT COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id;

  COUNT(*) MAX(EMPLOYEE_ID)
---------- ----------------
         1              200

Следующий вывод показывает, что оптимизатор выбрал наилучший план, сканирование индекса, на основе низкой оценки кардинальности текущего связанного значения, равного 10:

SQL> SELECT * from TABLE(DBMS_XPLAN.DISPLAY_CURSOR);

PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID	a9upgaqqj7bn5, child number 2
-------------------------------------
select COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id

Plan hash value: 1642965905

-------------------------------------------------------------------------------------
| Id| Operation                           | Name       |Rows|Bytes|Cost (%CPU)|Time |
-------------------------------------------------------------------------------------
| 0| SELECT STATEMENT                     |                   |  |  |2(100)|        |
| 1|  SORT AGGREGATE                      |                   |1 |8 |      |        |
| 2|   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES         |1 |8 |2  (0)|00:00:01|
|*3|    INDEX RANGE SCAN                  | EMP_DEPARTMENT_IX | 1|  |1  (0)|00:00:01|
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("DEPARTMENT_ID"=:DEPT_ID)

Вывод V$SQL теперь показывает, что есть три дочерних курсора:

SELECT SQL_TEXT, CHILD_NUMBER AS CHILD#, EXECUTIONS AS EXEC, 
       BUFFER_GETS AS BUFF_GETS, IS_BIND_SENSITIVE AS BIND_SENS, 
       IS_BIND_AWARE AS BIND_AWARE, IS_SHAREABLE AS SHAREABLE
FROM   V$SQL
WHERE  SQL_TEXT LIKE '%mployee%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';

SQL_TEXT               CHILD#  EXEC  BUFF_GETS BIND_SENS BIND_AWARE SHAREABLE
---------------------- ------ ----- ---------- --------- ---------- ---------
SELECT COUNT(*), MAX(e      0     2       1329         Y          N         N
mployee_id) FROM hr.em
ployees WHERE departme
nt_id = :dept_id

SELECT COUNT(*), MAX(e      1     1        800         Y          Y         Y
mployee_id) FROM hr.em
ployees WHERE departme
nt_id = :dept_id

SELECT COUNT(*), MAX(e      2     1          3         Y          Y         Y
mployee_id) FROM hr.em
ployees WHERE departme
nt_id = :dept_id

Когда курсор переключается в режим с поддержкой привязки, база данных отбрасывает исходный курсор (CHILD# из 0). Это разовые расходы. База данных помечает курсор 0 как недоступный для совместного использования (SHAREABLE равен N), что означает, что этот курсор недоступен и первым устаревает из кэша курсоров.

20.3.5 Слияние курсоров

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

В этом случае база данных объединяет курсоры, чтобы сэкономить место в библиотечном кеше. База данных увеличивает диапазон селективности курсора, чтобы включить селективность нового связанного значения.

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

20.3.6 Совместное использование адаптивных курсоров

Вы можете использовать представление V$ для адаптивного совместного использования курсора, чтобы просмотреть диапазоны селективности, информацию о курсоре (например, поддерживает ли курсор привязку или поддерживает привязку) и статистику выполнения.
В частности, используйте следующие представления:

  • V$SQL показывает, является ли курсор чувствительным к привязке или поддерживающим привязку.
  • V$SQL_CS_HISTOGRAM показывает распределение количества выполнений на гистограмме истории выполнения с тремя сегментами.
  • Если для проверки совместного использования курсора используется селективность, V$SQL_CS_SELECTIVITY показывает область избирательности, сохраненную для каждого предиката, включающего переменные связывания. Он содержит текст предиката, а также нижнее и верхнее значения диапазона селективности.
  • V$SQL_CS_STATISTICS обобщает информацию, используемую оптимизатором, чтобы определить, следует ли помечать курсор как поддерживающий привязку. В примере выполнения база данных отслеживает обработанные строки, выборки из буфера и время процессора. В столбце PEEKED отображается YES, если курсор построен с использованием набора привязок; в противном случае значение равно NO.

20.4 Реальные рекомендации по производительности для совместного использования курсора

Команда Real-World Performance разработала руководство по оптимизации совместного использования курсоров в приложениях Oracle Database.

20.4.1 Разработка приложений с переменными связывания для обеспечения безопасности и производительности

Real-World Performance Group настоятельно рекомендует использовать переменные связывания во всех корпоративных приложениях .

Приложения Oracle Database предназначены для написания с использованием переменных связывания. Избегайте проектов приложений , которые заставляют большое количество пользователей выполнять динамические неразделяемые операторы SQL .

Всякий раз, когда Oracle Database не может найти совпадение с оператором в библиотечном кэше, она должна выполнить жесткий синтаксический анализ. Несмотря на опасность разработки приложений с использованием литералов, не все реальные приложения используют переменные связывания. Разработчики иногда считают, что быстрее и проще писать программы, использующие литералы . Однако сокращение времени разработки не приводит к повышению производительности и безопасности после развертывания .

Основные преимущества использования переменных связывания заключаются в следующем:

  • Эффективность использования ресурсов
    При компиляции программы перед каждым ее выполнением ресурсы используются неэффективно, но на самом деле это именно то, что делает Oracle Database, когда выполняет жесткий анализ. Сервер базы данных должен потреблять много ресурсов ЦП и памяти для создания курсоров, создания и оценки планов выполнения и т. д. Делая базы данных общими курсорами, программный синтаксический анализ потребляет гораздо меньше ресурсов. Если приложение использует литералы вместо переменных связывания и выполняет лишь несколько запросов в день, администратор базы данных может не рассматривать дополнительные накладные расходы как проблему производительности. Однако если приложение выполняет сотни или тысячи запросов в секунду, дополнительные накладные расходы на ресурсы могут легко снизить производительность до неприемлемого уровня. Использование переменных связывания приводит к тому, что база данных выполняет жесткий синтаксический анализ только один раз, независимо от того, сколько раз выполняется оператор .

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

  • Пропускная способность и время отклика
    Когда база данных избегает постоянного повторного синтаксического анализа и создания курсоров, больше времени она проводит в пользовательском пространстве. Команда Real-World Performance обнаружила, что изменение литералов для использования привязок часто приводило к улучшению пропускной способности и времени отклика пользователя на несколько порядков.

  • Безопасность
    Единственный способ предотвратить атаки путем внедрения кода SQL — использовать переменные связывания. Злоумышленники могут использовать приложения строки подключения, «внедряя» код в приложение.

20.4.2 Не используйте CURSOR_SHARING = FORCE в качестве постоянного исправления

Лучшей практикой является написание совместно используемого SQL и использование EXACT по умолчанию для CURSOR_SHARING.

Однако для приложений со многими похожими операторами установка для CURSOR_SHARING значения FORCE иногда может значительно улучшить совместное использование курсора. Замена литералов связанными значениями, сгенерированными системой, снижает использование памяти, ускоряет синтаксический анализ и уменьшает конфликты защелок. Однако FORCE не является постоянным решением для разработки.

В качестве общего руководства Real-World Performance Group рекомендует не устанавливать для CURSOR_SHARING исключение FORCE в редких случаях и только в том случае, если выполняются все следующие условия:

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

Установка CURSOR_SHARING на FORCE имеет следующие недостатки:

  • Это указывает на то, что приложение не использует определяемые пользователем переменные связывания, что означает, что оно открыто для SQL-инъекций. Установка для CURSOR_SHARING значения FORCE не исправит ошибки SQL-инъекций и не сделает код более безопасным. Только после внедрения любого вредоносного текста SQL база данных привязывает значение.
  • Базе данных приходится выполнять дополнительную работу во время мягкого синтаксического анализа, чтобы найти похожие операторы в общем пуле.
  • База данных удаляет каждое слово, а значит, может удалить полезную информацию. Например, база данных убирает литеральные значения в функциях SUBSTR и TO_DATE. Использование сгенерированных системой переменных связывания, где литералы более оптимизированы, может негативно повлиять на план выполнения.
  • Максимальная длина (возвращаемая DESCRIBE) любого выбранного выражения, содержащего литералы в операторе SELECT, была увеличена. Однако фактическая длина возвращаемых данных не изменилась.
  • Преобразования звезд не поддерживаются.

20.4.3 Установите правила кодирования для увеличения повторного использования курсора

По умолчанию любое изменение текста двух операторов SQL не позволяет базе данных совместно использовать курсоры , включая имена переменных связывания. Кроме того, изменения размера переменных связывания могут вызвать несоответствие курсоров. Таким образом, использование переменных связывания в коде приложения недостаточно, чтобы гарантировать совместное использование курсора.

Команда Real-World Performance Team рекомендует стандартизировать соглашения о пробелах и использовании заглавных букв для операторов SQL и блоков PL/SQL . Также установите соглашения по именованию и определению переменных связывания . Если база данных не использует совместно курсоры, как ожидалось, запустите диагностику, запросив V$SQL_SHARED_CURSOR.

Пример 20-17 Изменения в тексте SQL

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

VARIABLE emp_id NUMBER
EXEC :emp_id := 101;

SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :EMP_ID;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :Emp_Id;
SELECT SUM(salary)  FROM hr.employees WHERE employee_id < :emp_id;
select sum(salary) from hr.employees where employee_id < :emp_id;
Select sum(salary) From hr.employees Where employee_id < :emp_id;
Select sum(salary) From hr.employees Where employee_id< :emp_id;

Запрос V$SQLAREA показывает, что совместное использование курсора не происходит:

COL SQL_TEXT FORMAT a35
SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE
FROM   V$SQLAREA
WHERE  SQL_TEXT LIKE '%mployee%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';

SQL_TEXT                            SQL_ID        VERSION_COUNT HASH_VALUE
----------------------------------- ------------- ------------- ----------
SELECT SUM(salary) FROM hr.employee bkrfu3ggu5315             1 3751971877
s WHERE employee_id < :EMP_ID
SELECT SUM(salary) FROM hr.employee 70mdtwh7xj9gv             1  265856507
s WHERE employee_id < :Emp_Id
Select sum(salary) From hr.employee 18tt4ny9u5wkt             1 2476929625
s Where employee_id< :emp_id
SELECT SUM(salary)  FROM hr.employe b6b21tbyaf8aq             1 4238811478
es WHERE employee_id < :emp_id
SELECT SUM(salary) FROM hr.employee 4318cbskba8yh             1  615850960
s WHERE employee_id < :emp_id
select sum(salary) from hr.employee 633zpx3xm71kj             1 4214457937
s where employee_id < :emp_id
Select sum(salary) From hr.employee 1mqbbbnsrrw08             1  830205960
s Where employee_id < :emp_id

7 rows selected.

Пример 20-18 Несоответствие длины привязки

Следующий код определяет переменную связывания с другой длиной (20 байтов против 100 байтов), а затем выполняет оператор с тем же текстом, используя то же значение связывания:

VARIABLE lname VARCHAR2(20)
EXEC :lname := 'Taylor';
SELECT SUM(salary) FROM hr.employees WHERE last_name = :lname;
VARIABLE lname VARCHAR2(100)
EXEC :lname := 'Taylor';
SELECT SUM(salary) FROM hr.employees WHERE last_name = :lname;

Следующий запрос показывает, что в базе данных нет общих курсоров:

COL SQL_TEXT FORMAT a35
SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE
FROM   V$SQLAREA
WHERE  SQL_TEXT LIKE '%mployee%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';

SQL_TEXT                            SQL_ID	       VERSION_COUNT HASH_VALUE
----------------------------------- ------------- ------------- ----------
SELECT SUM(salary) FROM hr.employee buh8j4557r0h1             2 1249608193
s WHERE last_name = :lname

Причина в длине привязки:

COL BIND_LENGTH_UPGRADEABLE FORMAT a15
SELECT s.SQL_TEXT, s.CHILD_NUMBER, 
       c.BIND_LENGTH_UPGRADEABLE
FROM   V$SQL s, V$SQL_SHARED_CURSOR c
WHERE  SQL_TEXT LIKE '%employee%'
AND    SQL_TEXT NOT LIKE '%SQL_TEXT%'
AND    s.CHILD_ADDRESS = c.CHILD_ADDRESS;

SQL_TEXT                            CHILD_NUMBER BIND_LENGTH_UPG
----------------------------------- ------------ ---------------
SELECT SUM(salary) FROM hr.employee            0 N
s WHERE last_name = :lname
SELECT SUM(salary) FROM hr.employee            1 Y
s WHERE last_name = :lname

20.4.4 Минимизация изменений на уровне сеанса в среде оптимизатора

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

Пример 20-19 Несоответствие среды

В этом примере показаны две инструкции с одинаковым текстом, но они не имеют общего курсора:

VARIABLE emp_id NUMBER

EXEC :emp_id := 110;

ALTER SESSION SET OPTIMIZER_MODE = FIRST_ROWS;
SELECT salary FROM hr.employees WHERE employee_id < :emp_id;
ALTER SESSION SET OPTIMIZER_MODE = ALL_ROWS;
SELECT salary FROM hr.employees WHERE employee_id < :emp_id;

Запрос для V$SQL_SHARED_CURSOR показывает несоответствие режима оптимизатора:

在这里插入代码片

рекомендация

отblog.csdn.net/stevensxiao/article/details/125263221