Различное поведение для REF CURSOR между Oracle 10g и 11g, когда присутствует уникальный индекс? - PullRequest
18 голосов
/ 05 января 2011

Описание

У меня есть хранимая процедура Oracle, которая выполняется в течение 7 лет или около того как локально на экземплярах разработки, так и на нескольких клиентских тестовых и производственных экземплярах под управлением Oracle 8, затем 9, затем 10 и недавно.11. Он работал стабильно до обновления до Oracle 11g.По сути, процедура открывает ссылочный курсор, обновляет таблицу и завершает работу.В 10g курсор будет содержать ожидаемые результаты, но в 11g курсор будет пустым.Никакие DML или DDL не изменились после обновления до 11g.Это поведение согласуется с каждым экземпляром 10g или 11g, который я пробовал (10.2.0.3, 10.2.0.4, 11.1.0.7, 11.2.0.1 - все работает в Windows).

Конкретный код намного сложнеено чтобы объяснить проблему в несколько реалистичном обзоре: у меня есть некоторые данные в таблице заголовков и несколько дочерних таблиц, которые будут выводиться в PDF.Таблица заголовков имеет логический столбец (NUMBER (1), где 0 - false, а 1 - true), указывающий, были ли обработаны эти данные.

Представление ограничено отображением только тех строк, которые не были обработаны (представление также объединяет некоторые другие таблицы, выполняет некоторые встроенные запросы и вызовы функций и т. Д.).Таким образом, в тот момент, когда курсор открывается, представление отображает одну или несколько строк, затем после открытия курсора выполняется оператор обновления, чтобы перевернуть флаг в таблице заголовков, выдается коммит, а затем процедура завершается.

На 10g, курсор открывается, он содержит строку, затем оператор обновления устанавливает флаг и выполнение процедуры во второй раз не даст никаких данных.

На 11g курсор никогда не содержит строки, как будто курсор не открывается до тех пор, пока не выполнится оператор обновления.

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

Обновление 1: Мне удалось отследить проблему до уникального ограничения.Кажется, что когда в 11g присутствует уникальное ограничение, проблема воспроизводится 100% времени, независимо от того, выполняю ли я код реального мира с реальными объектами или следующий простой пример.

Обновление 2: Мне удалось полностью исключить вид из уравнения.Я обновил простой пример, чтобы показать, что проблема существует даже при выполнении запросов непосредственно к таблице.

Простой пример

CREATE TABLE tbl1
(
  col1  VARCHAR2(10),
  col2  NUMBER(1)
);

INSERT INTO tbl1 (col1, col2) VALUES ('TEST1', 0);

/* View is no longer required to demonstrate the problem
CREATE OR REPLACE VIEW vw1 (col1, col2) 
AS 
SELECT col1, col2 
  FROM tbl1 
 WHERE col2 = 0;
*/

CREATE OR REPLACE PACKAGE pkg1
AS
   TYPE refWEB_CURSOR IS REF CURSOR;

   PROCEDURE proc1 (crs  OUT  refWEB_CURSOR);

END pkg1;

CREATE OR REPLACE PACKAGE BODY pkg1 
IS
   PROCEDURE proc1 (crs  OUT  refWEB_CURSOR)
   IS
   BEGIN

      OPEN crs FOR
        SELECT col1
          FROM tbl1
         WHERE col1 = 'TEST1'
           AND col2 = 0;

      UPDATE tbl1
         SET col2 = 1
       WHERE col1 = 'TEST1';

      COMMIT;

   END proc1;

END pkg1;

Демонстрация анонимного блока

DECLARE 
   crs1  pkg1.refWEB_CURSOR;

   TYPE rectype1 IS RECORD (
      col1  vw1.col1%TYPE
   );

   rec1  rectype1;
BEGIN 
   pkg1.proc1 ( crs1 );

   DBMS_OUTPUT.PUT_LINE('begin first test');

   LOOP
      FETCH crs1
       INTO rec1;

      EXIT WHEN crs1%NOTFOUND;

      DBMS_OUTPUT.PUT_LINE(rec1.col1);

   END LOOP;  

   DBMS_OUTPUT.PUT_LINE('end first test');

END; 

/* After creating this index, the problem is seen */
CREATE UNIQUE INDEX unique_col1 ON tbl1 (col1);

/* Reset data to initial values */
TRUNCATE TABLE tbl1;

INSERT INTO tbl1 (col1, col2) VALUES ('TEST1', 0);

DECLARE 
   crs1  pkg1.refWEB_CURSOR;

   TYPE rectype1 IS RECORD (
      col1  vw1.col1%TYPE
   );

   rec1  rectype1;
BEGIN 
   pkg1.proc1 ( crs1 );

   DBMS_OUTPUT.PUT_LINE('begin second test');

   LOOP
      FETCH crs1
       INTO rec1;

      EXIT WHEN crs1%NOTFOUND;

      DBMS_OUTPUT.PUT_LINE(rec1.col1);

   END LOOP;  

   DBMS_OUTPUT.PUT_LINE('end second test');

END; 

Примерчто будет на выходе 10g:
начать первый тест
TEST1
закончить первый тест
начать второй тест
TEST1
закончить второй тест

Пример того, чтовывод на 11g будет:
начать первый тест
ТЕСТ1
закончить первый тест
начать второй тест
закончить второй тест

Уточнение

Я могу 't удалите COMMIT, поскольку в сценарии реального мира процедура вызывается из веб-приложения.Когда поставщик данных на внешнем интерфейсе вызывает процедуру, он в любом случае выдает неявный COMMIT при отключении от базы данных.Так что, если я удалю COMMIT в процедуре, тогда да, демо анонимного блока будет работать, но сценарий реального мира не будет, потому что COMMIT все равно будет происходить.

Вопрос

Почему 11g ведет себя по-другому?Могу ли я что-нибудь сделать, кроме как переписать код?

Ответы [ 3 ]

7 голосов
/ 07 января 2011

Это, похоже, ошибка, обнаруженная сравнительно недавно. Металинк Баг 1045196 описывает точную проблему.Надеюсь, патч будет выпущен в ближайшее время.Для тех из вас, кто не может обойти стену Металинк, вот несколько подробностей:

Металинк

Ошибка 10425196: PL / SQL ВОЗВРАЩАЕТСЯ КУРСОР REF РАЗЛИЧНО НА 11.1.0.6 VS 10.2.0,5

Тип: Дефект
Серьезность: 2 - Серьезная потеря обслуживания
Статус: Ошибка кода Создано: 22 декабря 2010 года

ДИАГНОСТИЧЕСКИЙ АНАЛИЗ из оригинального представления случая:
- 10.2.0.4 Ожидаемое поведение Windows
- 10.2.0.5 Ожидаемое поведение Solaris
- 11.1.0.6 Неожиданное поведение Solaris
- 11.1.0.7 Неожиданное поведение Windows
- 11.2.0.1 Неожиданное поведение Solaris
- 11.2.0.2 Неожиданное поведение Solaris

ДОПОЛНИТЕЛЬНЫЕ ДАННЫЕ Я могу подтвердить :
- 10.2.0.3 Ожидаемое поведение Windows
- 11.2.0.1. Неожиданное поведение Windows

Дополнительные сведения

Изменение параметра OPTIMIZER_FEATURES_ENABLE = '10 .2.0.4 ' не решает проблему.Так что, похоже, это больше связано с изменением дизайна в ядре базы данных 11g, а не с настройкой оптимизатора.

Обход кода

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

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

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

    SELECT col1
      FROM tbl1
     WHERE UPPER(col1) = 'TEST1'
       AND col2 = 0;

Метод 2 : измените запрос, чтобы использовать подсказку, предотвращающую индексированиеиспользуемый.Можно ожидать, что подсказка NO_INDEX (unique_col1) сработает, но это не так.Подсказка RULE не работает.Вы можете использовать подсказку FULL (tbl1) , но, вероятно, это может замедлить ваш запрос больше, чем использование метода 1 .

    SELECT /*+ FULL(tbl1) */ col1
      FROM tbl1
     WHERE col1 = 'TEST1'
       AND col2 = 0;

Ответ Oracle и предлагаемый обходной путь

Служба поддержки Oracle наконец-то ответила следующим обновлением Metalink:

<b>Oracle Support - July 20, 2011 5:51:19 AM GMT-07:00 [ODM Proposed Solution(s)]</b>
Development has reported this will be a significant issue to fix and 
has suggested that the following workaround be applied:

edit init.ora/spfile with the following undocumented parameter:

"_row_cr" = false

<b>Oracle Support - July 20, 2011 5:49:20 AM GMT-07:00 [ODM Cause Justification]</b>
Development has determined this to be a defect

<b>Oracle Support - July 20, 2011 5:48:27 AM GMT-07:00 [ODM Cause Determination]</b>
Cause has been traced to a row source cursor optimization

<b>Oracle Support - July 20, 2011 5:47:27 AM GMT-07:00 [ODM Issue Verification]</b>
Development has confirmed this to be an issue in 11.2.0.1

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

<b>Oracle Support - July 21, 2011 5:58:07 AM GMT-07:00 [ODM Proposed Solution Justif]</b>
From 10.2.0.5 onward (which includes 11.2.0.2) we have an optimization called
ROW CR it is only applicable to queries which use an unique index to
determine the row in the table.

A brief overview of this optimization is that we try to avoid rollbacks while
constructing a CR block if the present block has no uncommitted changes.

So the difference seen in 11.2.0.2 is because of this optimization. The
suggested workaround is to turn off of this optimization so that things will
work exactly as they used to work in 10.2.0.4

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

2 голосов
/ 06 января 2011

Это действительно странная проблема, спасибо, что поделились!

Это действительно похоже на изменение поведения в Oracle, начиная с Oracle 11.1, и есть даже подтвержденная ошибка с похожей проблемой на metalink (ошибка # 10425196).К сожалению, на данный момент не имеется большой информации о металинке по теме, но я также открыл SR для Oracle с просьбой предоставить больше информации.

Хотя в настоящее время я не могу дать вам объяснение, почемупроисходит, и если есть (скрытый) параметр, который может повернуть это поведение обратно к стилю 10g, я думаю, что могу предоставить вам обходной путь.Вы можете использовать функциональность обратного запроса Oracle, чтобы заставить Oracle извлекать данные в ожидаемый момент времени.

Если вы измените свой код следующим образом:

OPEN crs FOR 
  SELECT col1
>>> FROM vw1 as of scn dbms_flashback.get_system_change_number
   WHERE col1 = 'TEST1';

, тогда результат должен быть таким же, как в 10 г.

И это упрощенная версия исходного теста:

SQL> drop table tbl1;
Table dropped
SQL> create table tbl1(col1 varchar2(10), col2 number);
Table created
SQL> create unique index tbl1_idx on tbl1(col1);
Index created
SQL> insert into tbl1(col1,col2) values('TEST1',0);
1 row inserted
SQL> DECLARE
  2    cursor web_cursor is
  3          SELECT col1
  4            FROM tbl1
  5           WHERE col2 = 0 and col1 = 'TEST1';
  6  
  7    rec1  web_cursor%rowtype;
  8  BEGIN
  9    OPEN web_cursor;
 10  
 11    UPDATE tbl1
 12       SET col2 = 1
 13     WHERE col1 = 'TEST1';
 14  
 15    -- different result depending on commit!
 16    commit;
 17  
 18     DBMS_OUTPUT.PUT_LINE('Start');
 19     LOOP
 20        FETCH web_cursor
 21         INTO rec1;
 22  
 23        EXIT WHEN web_cursor%NOTFOUND;
 24  
 25        DBMS_OUTPUT.PUT_LINE(rec1.col1);
 26     END LOOP;
 27     DBMS_OUTPUT.PUT_LINE('Finish');
 28  END;
 29  /

Start
Finish

PL/SQL procedure successfully completed

Если вы закомментируете коммит в строке 16, результат будет:

Start
TEST1
Finish

PL/SQL procedure successfully completed
1 голос
/ 07 февраля 2012

Из Металинка (он же Oracle Support)

Ошибка состояния 10425196: 92 - закрыт, а не ошибка

ПРОБЛЕМА:

При вызове хранимой процедуры, которая возвращает REF CURSOR, другое поведение замечен в 10.2.0.5 и более ранних версиях по сравнению с 11.1.0.6 и более поздними базами данных.

последовательность событий

  1. Вызов хранимой процедуры, передающей курсор Ref
  2. Курсор Open Ref против Таблицы A
  3. Обновление некоторых данных в таблице А изнутри хранимой процедуры
  4. COMMIT обновление
  5. Выполнение процедуры завершается, возвращая Ref Cursor обратно вызывающей стороне

10.2.0.5 и ранее

Возвращенный курсор не видит обновленные данные, так как он был открыт до данные обновляются. Это ожидаемое поведение.

11.1.0.6 и позже

Возвращенный курсор видит обновленные данные и возвращает обновленные данные, которые отличается от 10.2.0.5 и более раннего поведения.

ДИАГНОСТИЧЕСКИЙ АНАЛИЗ:

10.2.0.4 Ожидаемое поведение Windows 10.2.0.5 Ожидаемое поведение Solaris 11.1.0.6 Solaris Неожиданное поведение 11.1.0.7. Неожиданное поведение Windows 11.2.0.1 Solaris Неожиданное поведение 11.2.0.2 Неожиданное поведение Solaris

СВЯЗАННЫЕ ОШИБКИ:

Ничего не найдено.

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

_row_cr = false

...