Описание
У меня есть хранимая процедура 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 ведет себя по-другому?Могу ли я что-нибудь сделать, кроме как переписать код?