martes, 8 de abril de 2008

Vistas materializadas en Oracle (2)

Activar la reescritura de consultas

Las vista materializada pueden ser útiles para obtener datos precalculados, obtenidos a partir de funciones de agrupamiento como Max, Count, Avg... como en el siguiente ejemplo:

CREATE MATERIALIZED VIEW LOG ON t2
WITH PRIMARY KEY, ROWID, SEQUENCE ( t_key, amt );

CREATE MATERIALIZED VIEW mv
REFRESH FAST ON COMMIT
AS SELECT t_key, MAX( amt ) AS amt_max
FROM t2
GROUP BY t_key;

Con esta vista, podemos realizar consultas como:

SELECT t_key, amt_max
FROM mv
ORDER BY t_key ;
De forma mucho más rápida que con la consulta equivalente:
SELECT t_key, Max( amt ) AS amt_max
FROM t2
GROUP BY t_key
ORDER BY t_key;
Lo bueno del caso es que si tenemos activada en la base de datos una funcionalidad llamada reescritura de consultas (Query Rewrite), Oracle puede usar la información almacenada en la vista materializada para responder la segunda consulta de forma más rápida. Podemos activar esta reescritura realizando la siguiente acción sobre la vista materializada:
ALTER MATERIALIZED VIEW mv ENABLE QUERY REWRITE ;

Nota: El gestor de base de datos Oracle tiene que permitir esta posibilidad. Si no está activa la sentencia anterior producirá un error.

Por defecto, las vistas materializadas tienen la opción de reescritura desactivada.

Para que sea útil, necesitamos ahora recopilar estadísticas de uso que permitan a Oracle optimizar las consultas. Para ello, hacemos uso de las funciones definidas en el paquete DBMS_STATS. En nuestro ejemplo, realizaríamos la siguiente llamada:

EXECUTE DBMS_STATS.GATHER_TABLE_STATS( USER, 'mv' );

Vista materializada de joins

En el caso de crear una vista materializada de un join, se deben cumplir ciertas condiciones adicionales si queremos que sea de refresco rápido:

  • Deben existir logs o registros para cada tabla implicada en el join

  • La sentencia SELECT no debe contener objetos

  • No se pueden usar sentencias GROUP BY, ni funciones de agrupación.

  • Los campos ROWID de todas las tablas implicadas deben aparecer en la cláusula SELECT

Además de estas obligaciones, también hay unas cuantas recomendaciones:

  1. Como este tipo de vistas suele ser bastante grande, ya
    que no tiene agrupaciones, se puede aumentar mucho la
    eficacia definiendo un índice para cada campo ROWID de
    la vista.

  2. Después de haber creado la vista, se deben realizar un
    análisis estadístico usando el paquete DBMS_STATS. Oracle
    necesitas estas estadísticas para optimizar la reescritura
    de consultas, como ya se explicó.

Referencias


sábado, 5 de abril de 2008

Vistas materializadas en Oracle (1)

Según la wikipedia*, las vistas materializadas se definen así:

En un sistema de gestión de base de datos que siga el modelo relacional, una vista es una tabla virtual, que representa el resultado de una consulta. Siempre que se consulta o se actualiza una vista normal, el SGBD convierte estas operaciones en consultas o actualizaciones de las tablas usadas para definir la vista. Una vista materializada utiliza una aproximación diferente: el resultado de la consulta se almacena en una tabla cache real, que será actualizada de forma periódica a partir de las tablas originales. Esto proporciona un acceso mucho más eficiente, a costa de un incremento en el tamaño de la base de datos y a una posible falta de sincronía, es decir, que los datos de la vista pueden estar potencialmente desfasados con respecto a los datos reales. Es una solución muy utilizada en entornos de almacenes de datos (datawarehousing), donde el acceso frecuente a las tablas básicas resulta demasiado costoso.

Además, dado que la vista se almacena como una tabla real, se puede hacer con ella lo mismo que con cualquier otra tabla, siendo especialmente importante la capacidad de crear índices en cualquier columna, lo cual puede aumentar significativamente la velocidad de las consultas. En una vista normal, lo habitual es que sólo se permita utilizar índices sobre aquellas columnas que ya tienen definido un índice en la tabla original; a veces ni siquiera se ofrece esa posibilidad.

minority-report.0

Una vista materializada se define en base a una consulta a tablas, vistas y otras vistas materializadas. Generalmente se conoce a las tablas o vistas básicas como tablas maestras (si estamos hablando de replicación) o tablas detalle (si estamos hablando en términos de almacenes de datos). Es esta entrada hablaremos de tablas base, por considerar el uso de los términos maestro y detalle confusos.

Como crear una vista materializada

A la hora de crear una vista materializada en Oracle, debemos prestar especial atención al mecanismo de refresco; hay varias opciones: una de las más importantes es decidir si vamos a actualizar basándonos en las claves primarias o basándonos en el campo ROWID. Otra cuestión importante es si los refrescos van a ser automáticos o manuales. En el caso de ser automáticos, hay también varios parámetros importantes a tener en cuenta.

Veamos ahora las distintas opciones a la hora de definir el tipo de refresco.

Refresco completo

La forma más sencilla de especificar la forma en que los datos se actualizan desde las tablas básicas hasta la vista materializada es REFRESH COMPLETE. Con esta opción, se ejecuta la consulta que define la vista y se actualizan todos los datos, reemplazando la totalidad de los datos que hubiera antes.

Veamos el siguiente ejemplo:

CREATE MATERIALIZED VIEW mv
REFRESH COMPLETE
AS
SELECT * FROM t;

En este caso, no indicamos ninguna periodicidad, así que tenemos que hacer los refrescos manualmente. Para ello, se puede llamar al procedimiento almacenado DBMS_MVIEW.REFRESH, que acepta un primer parámetro list, que es una lista de vistas materializadas a refrescar, y un segundo parámetro method, al cual podemos pasarle el valor C para que realice un refresco completo:

EXECUTE DBMS_MVIEW.REFRESH( LIST => 'MV', METHOD => 'C' );

Evidentemente, si la vista materializada contiene muchos registros, y las tablas base cambian con poca frecuencia, reemplazar todos los datos cada vez que queramos refrescar no es nada recomendable. En estos casos, es mejor procesar solo aquellos registros que hayan cambiado; veremos esa posibilidad más adelante.

Refresco rápido

El sistema que permite actualizar sólo los registros que se hayan modificado se llama ´´FAST REFRESH``. Pero antes de poder crear una vista materializada con este sistema de refresco es necesario un mecanismo que registre dichos cambios en las tablas base. Este mecanismo se conoce como registro (log) de la vista materializada, y debemos crear dicho registro antes de poder crear la vista.

Pare crear el registro, usamos la sentencia CREATE MATERIALIZED VIEW LOG:

CREATE MATERIALIZED VIEW LOG ON t ;

No es necesario darle un nombre, porque una tabla solo puede tener un registro. El registro es una tabla, y se puede inspeccionar como cualquier otra, aunque el la práctica el desarrollador no tienen ninguna necesidad de referenciar a esta tabla. En cualquier caso, podemos consultar la estructura con la sentencia:

DESCRIBE MLOG$_T

El propósito del registro es almacenar la información de las modificaciones que sufra la tabla, identificando así los registros que deben ser refrescados. A la hora de identificar los registros, se pueden usar dos esquemas diferentes: basarnos en las claves primarias de la tabla o basarnos en los campos ROWID.

Para utilizar los campos de la clave primaria, hay que incluir WITH PRIMARY KEY a la hora de crear el registro, o no indicar nada ya que este es el valor que se asume por defecto:

CREATE MATERIALIZED VIEW LOG ON t WITH PRIMARY KEY;

En caso de querer utilizar el campo ROWID podemos usar WITH ROWID:

CREATE MATERIALIZED VIEW LOG ON t WITH ROWID;

También se puede especificar una columna especial, de tipo secuencia, en el registro. Esto permite a Oracle aplicar las actualizaciones en la vista materializada en el orden correcto. Esto puede ser importante si se mezclan ordenes de inserciones, actualizaciones y borrados en una misma transacción. La forma de especificar este comportamiento es:

CREATE MATERIALIZED VIEW LOG ON t WITH SEQUENCE;

Dado que dicha mezcla de operaciones es lo más habitual, es recomendable crear nuestros registros con dicha opción.

Por último, tenemos la opción de definir aquellos campos cuyos cambios queremos monitorizar, con la opción WITH <Column List>. Solo los cambios en los campos especificados modificaran el registro:

CREATE MATERIALIZED VIEW LOG ON t WITH ( A, B, C );

Hay más opciones a la hora de crear el registro. Véase las referencias al final para más información.

Ahora, por fin, una vez creado el registro o log de cambios, podemos crear la vista materializada con la opción REFRESH FAST. En ejemplo seria:

CREATE MATERIALIZED VIEW LOG ON t WITH SEQUENCE ;

CREATE MATERIALIZED VIEW mv
REFRESH FAST
AS SELECT * FROM t;

Lo forma de actualización que el gestor asume por omisión, es decir, si no indicamos nada, es la llamada REFRESH FORCE; una combinación de los dos modos. Realiza un refresco FAST si es posible, y en caso contrario realiza un refresco COMPLETE.

Como antes, podemos forzar una actualización llamando al procedimiento DBMS_MVIEW.REFRESH, solo que esta vez usaremos como segundo parámetro el valor 'F' (De Fast):

EXECUTE DBMS_MVIEW.REFRESH( LIST => 'mv', METHOD => 'F' );

Aun cuando se haya definido con FAST REFRESH, podemos realizar un refresco completo, si lo consideremos oportuno, usando el valor C:

EXECUTE DBMS_MVIEW.REFRESH( LIST => 'mv', METHOD => 'C' );

De forma similar, una vista materializada creada con la opción COMPLETE REFRESH puede ser actualizada incrementalmente, aunque sólo si creamos los registros o logs necesarios en las tablas básicas.

Cuando actualizar

Hasta el momento no hemos dicho nada de cuando se deben refrescar los datos, y de hecho los hemos refrescado manualmente en los ejemplos mostrados. Este es de nuevo el comportamiento por defecto, y lo podemos incluir en la definición de la vista materializada con la sentencia REFRESH ON DEMAND:


CREATE MATERIALIZED VIEW mv
REFRESH ON DEMAND
AS SELECT * FROM t;

Para provocar estas actualizaciones, podemos utilizar los siguientes procedimientos:


  • DBMS_MVIEW.REFRESH

  • DBMS_MVIEW.REFRESH_ALL_MVIEWS

  • DBMS_MVIEW.REFRESH_DEPENDENT

La otra posibilidad es hacer que Oracle refresque la vista materializada automáticamente cada vez que se hace un commit a las tablas base. Eso se puede hacer incluyendo COMMIT en la sentencia REFRESH, como en el siguiente ejemplo:

CREATE MATERIALIZED VIEW mv
REFRESH FAST ON COMMIT
AS SELECT * FROM t;

No obstante, la actualización ON COMMIT solo es posible en determinadas situaciones:


  • La vista materializada debe ser definida con refresco rápido (FAST)

  • La vista materializada no puede contener campos de tipo objeto ni tipos adicionales de Oracle

  • Las tablas básicas implicadas nunca deben formar parte de transacciones distribuidas

Las dos primeras condiciones producirán un error en tiempo de compilación. El tercer caso producirá el error cuando se intente realizar una transacción distribuida en alguna tabla base.

Otra posibilidad es definir actualizaciones periódicas, que sean realizadas automáticamente por el sistema pero que demanden menos recursos que las actualizaciones ON COMMIT. Para ello se le indica el momento de carga inicial de los datos con la sentencia START WITH (normalmente SYSDATE, es decir, ahora mismo, pero se puede especificar cualquier momento en el futuro) y una frecuencia de actualización con la sentencia NEXT. Por ejemplo, para definir una vista materializada que se actualice cada media hora, haríamos:


CREATE MATERIALIZED VIEW mv
REFRESH FAST
START WITH SYSDATE
NEXT SYSDATE + 1/48
WITH PRIMARY KEY
AS SELECT * FROM t;

La clausula REFRESH

En resumen, la sintaxis de la cláusula REFRESH es la siguiente:


REFRESH [fast|complete|force]
[on demand | commit]
[start with date] [next date]
[with {primary key|rowid}]

Referencias