Oracle 8i динамическая ошибка SQL на подвыборах в блоках pl / sql - PullRequest
0 голосов
/ 02 апреля 2012

Я написал функцию Oracle (для 8i) для выборки строк, затронутых оператором DML, эмулируя поведение RETURNING * из PostgreSQL.Типичный вызов функции выглядит следующим образом:

SELECT tablename_dml('UPDATE tablename SET foo = ''bar''') FROM dual;

Функция создается автоматически для каждой таблицы и использует Dynamic SQL для выполнения запроса, переданного в качестве аргумента.Более того, оператор, который выполняет запрос динамически, также заключен в блок BEGIN .. END:

EXECUTE IMMEDIATE 'BEGIN'||query||' RETURNING col1, col2 BULK COLLECT INTO :1, :2;END;' USING OUT col1_t, col2_t;

Причина этой специфической конструкции заключается в том, что она кажется единственным способомчтобы получить значения из оператора DML, который влияет на несколько строк.И col1_t, и col2_t объявлены как коллекции типов, соответствующих столбцам таблицы.

Наконец, к проблеме.Когда переданный запрос содержит подвыбор, выполнение функции вызывает синтаксическую ошибку.Ниже приведен простой пример, иллюстрирующий это:

CREATE TABLE xy(id number, name varchar2(80));
CREATE OR REPLACE FUNCTION xy_fn(query VARCHAR2) RETURN NUMBER IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
EXECUTE IMMEDIATE 'BEGIN '||query||'; END;';
ROLLBACK;
RETURN 5;
END;
SELECT xy_fn('update xy set id = id + (SELECT min(id) FROM xy)') FROM DUAL;

Последнее выражение выдает следующую ошибку: (упомянутое SELECT - это SELECT min (id))

ORA-06550: строка 1, столбец 32: PLS-00103: Обнаружен символ «ВЫБОР» при ожидании одного из следующих действий: (- - + mod не нулевой, другие существует среднее число счетчиков, макс. Мин. Мин.date

Эта проблема возникает на 8i (8.1.6), но не на 10 г. Если блоки BEGIN .. END удалены - проблема исчезает. Если подзапрос в запросе заменяется чем-то другим,то есть константа, проблема исчезает.

К сожалению, я застрял с 8i и удаляю BEGIN .. END не вариант (см. объяснение выше).

Есть ли конкретный Oracle8i ограничение в игре здесь? Можно ли преодолеть его с помощью динамического SQL?

Ответы [ 2 ]

2 голосов
/ 02 апреля 2012

Не знаю, зачем вам нужно делать всю эту работу.Oracle 8i поддерживает возврат в массовую коллекцию. Узнать больше

Таким образом, вы просто сможете выполнить этот оператор в нединамическом SQL.Как то так:

UPDATE tablename 
SET foo = 'bar' 
returning  col1, col2 bulk collect into col1_t, col2_t;
1 голос
/ 03 апреля 2012

Лишенный всех несоответствий, я думаю, ваш вопрос прост.

Этот оператор обновления выполняется в SQL:

update xy set id = id + (SELECT min(id) FROM xy);

И этот анонимный блок также запускается:

begin
    update xy set id = id + 100;
end;

Но объединение двух не работает:

begin
    update xy set id = id + (SELECT min(id) FROM xy);
end;

Возможно, вы столкнулись с ограничением более старого Oracle. До 9i механизм SQL и механизм PL / SQL SQL всегда были не синхронизированы. Поэтому последние функции, поддерживаемые в SQL, часто не поддерживаются в PL / SQL. Кажется, у вас есть один из них.

Поскольку 9i Oracle стремилась поддерживать синхронизацию двух механизмов, поэтому гораздо реже находить вещи, которые работают в SQL, но не в PL / SQL.

Учитывая характер вашей задачи, обновление вашей версии Oracle вышло. Итак, все, что я могу предложить, это то, что у вас есть две процедуры, одна из которых поддерживает синтаксис подзапроса (избегая необходимости в таких подзапросах. Примерно так:

CREATE OR REPLACE FUNCTION xy_sqfn
    (main_query VARCHAR2
      , sub_query VARCHAR2   ) 
    RETURN NUMBER 
IS      
    n pls_integer;
BEGIN         
    execute immediate sub_query into n;
    EXECUTE IMMEDIATE 'BEGIN '||main_query||'; END;'
          using n;
    RETURN 5; 
END;

Назовите это так

result := xy_sqfn ('update xy set id = id + :1'
                   , 'SELECT min(id) FROM xy');

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


Кстати, использование прагмы AUTONOMOUS TRANSACTION для того, чтобы помешать выполнению DML в операторе SELECT, довольно ужасно. Почему бы просто не запустить функции в PL / SQL? Или использовать процедуры? Полагаю, вы скажете, что это не имеет значения, потому что вы просто пишете какой-то неуклюжий код для поддержки миграции данных. Это достаточно справедливо, но для будущих искателей: не делайте этого! Это очень плохая практика!

...