2012-03-29 10 views
13

Recientemente hemos actualizado de Oracle 10 a Oracle 11.2. Después de la actualización, comencé a ver un error de tabla de mutación causado por una función en lugar de un disparador (que nunca he encontrado antes). Es código antiguo que funcionaba en versiones anteriores de Oracle.Tabla de Mutación en Oracle 11 causada por una función

He aquí un escenario que hará que el error:

create table mutate (
    x NUMBER, 
    y NUMBER 
); 

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

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

He creado dos filas. Ahora, voy a duplicar mis filas llamando a esta declaración:

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

Esto no es estrictamente necesario duplicar el error, pero ayuda con mi demostración más tarde. Entonces el contenido de la tabla ahora se ve así:

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

Todo está bien. Ahora, para la parte divertida:

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; 
/

He creado una función para consultar mi tabla y devolver un conteo. Ahora, combinaré eso con una instrucción INSERT:

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

¿El resultado? Este error:

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

así que sé lo que causa el error, pero estoy curioso en cuanto a por qué el . ¿Oracle no está ejecutando SELECT, recuperando el conjunto de resultados, y luego realizando una inserción masiva de esos resultados? Solo esperaría un error de tabla mutante si los registros ya se estaban insertando antes de que la consulta finalizara. Pero si Oracle hizo eso, no lo haría la declaración anterior:

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

empezar un bucle infinito?

ACTUALIZACIÓN:

a través del enlace de Jeffrey encontré esto en 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.

Hay también un comentario del autor en 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.

Estoy en lo cierto al suponer que este el comportamiento ha cambiado entre las versiones de Oracle 10 y 11?

Respuesta

9

En primer lugar,

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

no se inicia un bucle infinito, porque la consulta no verá los datos que se inserta - únicos datos que existió a partir del inicio de la instrucción. Las nuevas filas solo serán visibles para las siguientes declaraciones.

This lo explica muy bien:

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

¡Buen enlace, gracias! Voy a actualizar mi pregunta y dejarla abierta un momento para ver si genera alguna discusión adicional. –

+0

+1 usted entendió por qué sucede – zep

+0

Esto ayudó, pero tiene una pregunta de aclaración ... ¿Puede la función existir en un paquete que se llama desde un activador de actualización anterior? Estoy teniendo una pesadilla de problemas como muchos otros debido a los desencadenantes de mierda de Oracles. –

8

Declaración de coherencia de lectura y Transacción coherencia de lectura".

Del manual:

"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".

Ambos conceptos se explican en los "Conceptos Oracle® Database":

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


- >>>ACTUALIZACIÓN

- >>> * Sección añadió después el OP se cerró

La regla

La norma técnica, bien comunicado por el Sr. Kemp ([email protected] Jeffrey) y bien explicado por Toon Koppelaars here, se informa en "referencia Pl/lenguaje SQL - control de los efectos secundarios de PL/SQL subprogramas" (su función viola DNR lee ninguna base de datos de estado):

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

¡Es bueno saberlo! Esa parece ser otra buena razón para evitar las llamadas a funciones dentro de las sentencias de SQL tanto como sea posible, más allá de la consideración del rendimiento: violan la regla de instantáneas de nivel de SQL padre. –

+0

@ Dan-A. Los uso para simplificar cálculos complejos sobre valores de fila en funciones determinísticas donde no violan WNDS y RNDS (no escriben estado de datos y no leen ningún estado de datos). – zep

Cuestiones relacionadas