2012-03-29 10 views
13

Abbiamo recentemente aggiornato da Oracle 10 a Oracle 11.2. Dopo l'aggiornamento, ho iniziato a vedere un errore di tabella mutante causato da una funzione piuttosto che da un trigger (che non ho mai visto prima). È il vecchio codice che funzionava nelle versioni precedenti di Oracle.Tabella muting in Oracle 11 causata da una funzione

Ecco uno scenario che causerà l'errore:

create table mutate (
    x NUMBER, 
    y NUMBER 
); 

insert into mutate (x, y) 
values (1,2); 

insert into mutate (x, y) 
values (3,4); 

ho creato due file. Ora, io raddoppiare i miei file chiamando questa affermazione:

insert into mutate (x, y) 
select x + 1, y + 1 
from mutate; 

Questo non è strettamente necessario duplicare l'errore, ma si aiuta con la mia dimostrazione più tardi. Quindi il contenuto della tabella ora appare così:

X,Y 
1,2 
3,4 
2,3 
4,5 

Tutto va bene. Ora per la parte divertente:

create or replace function mutate_count 
return PLS_INTEGER 
is 
    v_dummy PLS_INTEGER; 
begin 
    select count(*) 
    into v_dummy 
    from mutate; 

    return v_dummy; 
end mutate_count; 
/

Ho creato una funzione per interrogare la mia tabella e restituire un conteggio. Ora, lo combinerò con una dichiarazione INSERT:

insert into mutate (x, y) 
select x + 2, y + 2 
from mutate 
where mutate_count() = 4; 

Il risultato? Questo errore:

ORA-04091: table MUTATE is mutating, trigger/function may not see it 
ORA-06512: at "MUTATE_COUNT", line 6 

Quindi so che cosa causa l'errore, ma sono curioso di sapere il motivo per cui . Oracle non sta eseguendo SELECT, recuperando il set di risultati e quindi eseguendo un inserimento di questi risultati? Mi aspetterei solo un errore di tabella mutante se i record fossero già stati inseriti prima della fine della query. Ma se Oracle ha fatto questo, non sarebbe la dichiarazione precedente:

insert into mutate (x, y) 
select x + 1, y + 1 
from mutate; 

avviare un ciclo infinito?

UPDATE:

tramite link di Jeffrey Ho trovato questo in the Oracle docs:

By default, Oracle guarantees statement-level read consistency. The set of data returned by a single query is consistent with respect to a single point in time.

C'è anche un commento da parte dell'autore in his post:

One could argue why Oracle doesn't ensure this 'statement-level read consistency' for repeated function calls that appear inside a SQL statement. It could be considered a bug as far as I'm concerned. But this is the way it currently works.

Ho ragione nel ritenere che questo comportamento è cambiato tra Oracle versioni 10 e 11?

risposta

9

In primo luogo,

insert into mutate (x, y) 
select x + 1, y + 1 
from mutate; 

Non inizia un ciclo infinito, perché la query non vedrà i dati che è stato inserito - solo i dati che esisteva come dell'inizio della dichiarazione. Le nuove righe saranno visibili solo alle dichiarazioni successive.

This spiega abbastanza bene:

When Oracle steps out of the SQL-engine that's currently executing the update statement, and invokes the function, then this function -- just like an after row update trigger would -- sees the intermediate states of EMP as they exist during execution of the update statement. This implies that the return value of our function invocations heavily depend on the order in which the rows happen to be updated.

+0

buon collegamento, grazie! Ho intenzione di aggiornare la mia domanda e lasciarla aperta per un po 'per vedere se genera discussioni aggiuntive. –

+0

+1 hai capito il motivo per cui succede – zep

+0

Questo ha aiutato, ma hai una domanda di chiarimento ... La funzione può esistere in un Pacchetto che viene chiamato da un trigger di aggiornamento precedente? Sto avendo un incubo di problemi come molti altri a causa di oracoli trigger di oracoli. –

8

a livello di istruzione Leggi Coerenza e delle transazioni a livello di consistenza in lettura".

Dal manuale:

"If a SELECT list contains a function, then the database applies statement-level read consistency at the statement level for SQL run within the PL/SQL function code, rather than at the parent SQL level. For example, a function could access a table whose data is changed and committed by another user. For each execution of the SELECT in the function, a new read consistent snapshot is established".

Entrambi i concetti sono spiegati nella sezione "Concetti di database Oracle®":

http://download.oracle.com/docs/cd/B19306_01/server.102/b14220/consist.htm#sthref1955


- >>>UPDATE

- >>> * Sezione aggiunto dopo laPO è stato chiuso

La regola

La regola tecnica, ben collegato dal sig Kemp (@ Jeffrey-Kemp) e ben spiegato da Toon Koppelaars here, è riportato in "riferimento Pl/Sql lingua - controllo degli effetti collaterali di PL/SQL Sottoprogrammi" (la funzione viola RNDS leggono senza stato del database):

When invoked from an INSERT, UPDATE, or DELETE statement, the function cannot query or modify any database tables modified by that statement.

If a function either queries or modifies a table, and a DML statement on that table invokes the function, then ORA-04091 (mutating-table error) occurs.

PL/SQL Functions that SQL Statements Can Invoke

+0

Buono a sapersi! Sembra un altro buon motivo per evitare il più possibile le chiamate di funzione all'interno di istruzioni SQL, al di là della considerazione prestazionale: violano la regola snapshot di livello SQL genitore. –

+0

@ Dan-A. Li uso per semplificare calcoli complessi su valori di riga in funzioni deterministiche dove non violano WNDS e RNDS (non scrivono nessuno stato di dati e non leggono nessuno stato di dati). – zep