2010-11-04 13 views
6

Non riesco a convincere perché non è possibile aggiungere operazioni DML all'interno di Funzione Oracle in particolare all'interno del ciclo di cursore . Ritengo che Oracle non supporti l'operazione DML all'interno del ciclo del cursore.non può eseguire operazioni DML all'interno di una query

Come posso fare Se devo inserire nella tabella all'interno del ciclo del cursore? Crea una nuova procedura di memorizzazione al suo interno o qualcos'altro?

messaggio di errore: Impossibile eseguire l'operazione DML all'interno di una query

Ecco la mia funzione,

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; 

risposta

11

È possibile uso DML all'interno di una funzione di PL/SQL - nessun problema. Tuttavia, la funzione può essere chiamata solo da PL/SQL, non da SQL - vale a dire che può essere chiamato in questo modo:

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

... ma non come questo:

select test_func(empno) from emp; 

Che porta a il messaggio di errore che hai postato.

Molte persone (incluso me) non amano le funzioni che hanno "effetti collaterali" come questo, ma è una questione di best practice e standard, non un problema tecnico.

+0

Grazie, Questo è quello che sto chiedendo. – ppshein

11

È possibile eseguire operazioni DML all'interno di una funzione Oracle PL/SQL e, sebbene in genere non sia una buona pratica, chiamarlo da SQL. La funzione deve essere contrassegnata con un pragma AUTONOMOUS_TRANSACTION e la transazione deve essere confermata o ripristinata prima di uscire dalla funzione (vedere AUTONOMOUS_TRANSACTION Pragma).

Si dovrebbe essere consapevoli del fatto che questo tipo di funzione chiamata da SQL può drammaticamente ridurre le prestazioni delle query. Vi consiglio di usarlo solo per scopi di revisione.

Ecco uno script di esempio a partire dalla vostra funzione:

 
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; 

Quando si esegue lo script precedente si ottiene il seguente risultato:

 
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 
+2

+1. Oltre alle prestazioni e ai problemi di transazione è anche molto difficile sapere esattamente quante volte la funzione verrà eseguita. Ad esempio, questa istruzione non chiama affatto la funzione: selezionare * da duale dove esiste (selezionare test_func ('KING') da dual); SQL è dichiarativo e non c'è modo di garantire esattamente come verrà eseguita una query. –

Problemi correlati