Resumen
creo que la actualización a nula es más lento debido a Oracle (incorrectamente) trata de aprovechar la manera en que almacena los nulos, haciendo que con frecuencia reorganizar las filas en el bloque ("bloque del montón compress "), creando una gran cantidad de UNDO y REDO extra.
¿Qué tiene de especial la nulidad?
Desde el Oracle Database Concepts:
"valores nulos se almacenan en la base de datos si caen entre las columnas con valores de datos en estos casos que requieren de 1 byte para almacenar la longitud de la columna (cero)
..
Los nulos finales en una fila no requieren almacenamiento porque un nuevo encabezado de fila indica que las columnas restantes en la fila anterior son nulas. Por ejemplo, si las últimas tres columnas de una tabla son nulas, no se almacena información para esas columnas. con muchas columnas, , las columnas con más probabilidades de contener nulos deberían definirse al final para conservar el espacio en disco ".
prueba
actualizaciones de la evaluación comparativa es muy difícil, porque el verdadero costo de una actualización no se puede medir sólo de la instrucción de actualización. Por ejemplo, los conmutadores de registro no suceden con cada actualización, y la eliminación retardada de bloques ocurrirá más tarde. Para probar con precisión una actualización, debe haber varias ejecuciones, los objetos deben recrearse para cada ejecución, y los valores altos y bajos deben descartarse.
Para simplificar, el siguiente script no arroja resultados altos y bajos, y solo prueba una tabla con una sola columna. Pero el problema aún ocurre independientemente del número de columnas, sus datos y qué columna se actualiza.
Utilicé la utilidad RunStats de http://www.oracle-developer.net/utilities.php para comparar el consumo de recursos de actualización-a-un-valor con la actualización-a-un-nulo.
create table test1(col1 number);
BEGIN
dbms_output.enable(1000000);
runstats_pkg.rs_start;
for i in 1 .. 10 loop
execute immediate 'drop table test1 purge';
execute immediate 'create table test1 (col1 number)';
execute immediate 'insert /*+ append */ into test1 select 1 col1
from dual connect by level <= 100000';
commit;
execute immediate 'update test1 set col1 = 1';
commit;
end loop;
runstats_pkg.rs_pause;
runstats_pkg.rs_resume;
for i in 1 .. 10 loop
execute immediate 'drop table test1 purge';
execute immediate 'create table test1 (col1 number)';
execute immediate 'insert /*+ append */ into test1 select 1 col1
from dual connect by level <= 100000';
commit;
execute immediate 'update test1 set col1 = null';
commit;
end loop;
runstats_pkg.rs_stop();
END;
/
Resultado
Hay docenas de diferencias, estos son los cuatro Creo que son los más relevantes:
Type Name Run1 Run2 Diff
----- ---------------------------- ------------ ------------ ------------
TIMER elapsed time (hsecs) 1,269 4,738 3,469
STAT heap block compress 1 2,028 2,027
STAT undo change vector size 55,855,008 181,387,456 125,532,448
STAT redo size 133,260,596 581,641,084 448,380,488
Soluciones?
La única solución posible que se me ocurre es habilitar la compresión de tablas. El truco de almacenamiento nulo al final no ocurre para las tablas comprimidas. Por lo tanto, aunque el número de "comprimir bloques de bloques" es aún mayor para Run2, de 2028 a 23208, creo que en realidad no hace nada. El tiempo de rehacer, deshacer y transcurrido entre las dos ejecuciones es casi idéntico a la compresión de tabla habilitada.
Sin embargo, existen muchas desventajas potenciales en la compresión de tablas. La actualización a un nulo se ejecutará mucho más rápido, pero cada otra actualización se ejecutará al menos un poco más lento.
¿Puede publicar su plan de ejecución/explicación? – diagonalbatman
¿Hay una cláusula 'where'? – Johan
Si reviso toda la tabla de una vez, no hay cláusula where. Si reviso la tabla fila por fila, aparece una cláusula where que hace referencia a la clave principal de la tabla. El resultado permanece igual en ambas versiones. En cuanto al plan de ejecución, prepararé uno y un ejemplo paso a paso para reproducir el resultado el día de hoy. –