не может выполнить операцию DML внутри запроса - PullRequest
6 голосов
/ 04 ноября 2010

Я не могу убедить, почему я не могу добавить операцию DML внутри Функция Oracle , особенно внутри цикл курсора . Я чувствую, что Oracle не поддерживает работу DML внутри цикла курсора.

Как это сделать, если мне нужно вставить в таблицу внутри курсора цикл? Создать новую процедуру магазина внутри нее или что-то еще?

Сообщение об ошибке: не может выполнить операцию DML внутри запроса

Вот моя функция,

CREATE OR REPLACE FUNCTION TEST_FUNC(U_ID IN VARCHAR2)
RETURN VARCHAR2
IS
  V_MESSAGE VARCHAR2(30);
  CURSOR C_PERSON (V_ID VARCHAR2) IS
         SELECT NAME_UPPER
         FROM TBL_PERSON
         WHERE NAME_UPPER = V_ID;                  
BEGIN
   FOR C_PERSON_CURSOR IN C_PERSON(U_ID) 
   LOOP
       INSERT INTO TMP_PERSON(NAME) VALUES (C_PERSON_CURSOR.NAME_UPPER);
   END LOOP;

   RETURN V_MESSAGE;

EXCEPTION
WHEN OTHERS THEN
    raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
END;

Ответы [ 2 ]

12 голосов
/ 04 ноября 2010

Вы можете использовать DML внутри функции PL / SQL - нет проблем. Однако функцию можно вызывать только из PL / SQL, а не из SQL - то есть ее можно вызывать так:

declare
   l_message varchar2(30);
begin
   l_message := test_func('123');
end;

... но не так:

select test_func(empno) from emp;

Это приводит к опубликованному вами сообщению об ошибке.

Многим людям (включая меня) не нравятся функции, которые имеют подобные «побочные эффекты», но это вопрос передового опыта и стандартов, а не технический вопрос.

11 голосов
/ 04 ноября 2010

Вы можете выполнять операции DML внутри функции Oracle PL / SQL и, , хотя обычно это не очень хорошая практика, вызывать ее из SQL . Функция должна быть помечена прагмой AUTONOMOUS_TRANSACTION, а транзакция должна быть зафиксирована или откатана до выхода из функции (см. AUTONOMOUS_TRANSACTION Pragma ).

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

Вот пример скрипта, начинающегося с вашей функции:

CREATE TABLE TBL_PERSON (NAME_UPPER VARCHAR2(30));
CREATE TABLE TMP_PERSON (NAME VARCHAR2(30));

INSERT INTO TBL_PERSON (NAME_UPPER) VALUES ('KING');

CREATE OR REPLACE FUNCTION TEST_FUNC(U_ID IN VARCHAR2)
RETURN VARCHAR2
IS
  PRAGMA AUTONOMOUS_TRANSACTION; -- Needed to be called from SQL

  V_MESSAGE VARCHAR2(2000);
  CURSOR C_PERSON (V_ID VARCHAR2) IS
         SELECT NAME_UPPER
         FROM TBL_PERSON
         WHERE NAME_UPPER = V_ID;                  
BEGIN
   FOR C_PERSON_CURSOR IN C_PERSON(U_ID) 
   LOOP
       INSERT INTO TMP_PERSON(NAME) VALUES (C_PERSON_CURSOR.NAME_UPPER);

       V_MESSAGE := SQL%ROWCOUNT
          || ' Person record successfully inserted into TMP_PERSON table';
   END LOOP;

   COMMIT; -- The current autonomous transaction need to be commited
           -- before exiting the function.

   RETURN V_MESSAGE;

EXCEPTION
WHEN OTHERS THEN
    ROLLBACK;
    raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
END;
/

PROMPT Call the TEST_FUNC function and insert a new record into TMP_PERSON table
SELECT TEST_FUNC('KING') FROM DUAL;

PROMPT Content of the TMP_PERSON table
COL NAME FOR A30
SELECT * FROM TMP_PERSON;

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

Table created.

Table created.

1 row created.

Function created.

Calling the TEST_FUNC function and insert a new record into TMP_PERSON table

TEST_FUNC('KING')
------------------------------------------------------------
1 Person record successfully inserted into TMP_PERSON table

Content of the TMP_PERSON table

NAME
------------------------------
KING
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...