Real-Time Refresh(Vistas Materializadas) en Oracle
- Normal:
CREATE MATERIALIZED VIEW nombre-vista
BUILD [IMMEDIATE | DEFERRED]
REFRESH [FAST | COMPLETE | FORCE ]
ON [COMMIT | DEMAND ]
[[ENABLE | DISABLE] QUERY REWRITE]
AS
SELECT ...;
Preconstruida(Pre-Build):
CREATE MATERIALIZED VIEW nombre-vista
ON PREBUILT TABLE
REFRESH [FAST | COMPLETE | FORCE ]
ON [COMMIT | DEMAND ]
[[ENABLE | DISABLE] QUERY REWRITE]
AS
SELECT ...;
- IMMEDIATE: La vista materializada se llena de immediato.
- DEFERRED: La vista materializada se completa en la primera actualización solicitada.
- FAST: Se intenta un refresh rápido. Si los materialized logs no se encuentran en las tablas, falla.
- COMPLETE: el segmento de tabla que admite la vista materializada se trunca y se vuelve a llenar por completo utilizando la consulta asociada.
- FORCE: se intenta un refresh rápido. Si no es posible, se realiza una actualización completa.
- ON COMMIT: el refresh es desencadenado por un cambio en la data confirmado.
- ON DEMMAND: el refresh es iniciado mediante una solicitud manual o programada.
Ahora, crearemos una tabla de prueba que actuará como una tabla base en este ejemplo:
CREATE TABLE lineas_de_pedido (
id NUMBER(10),
id_orden NUMBER(10),
cantidad_linea NUMBER(5),
total NUMBER(10,2),
fecha_creacion DATE,
CONSTRAINT orders_pk PRIMARY KEY (id)
);
Inserción de datos: Para que se pueda ver la utilizad de una vista materializada, vamos a llenar la tabla con 100000 filas de datos aleatorios. Nota: no se debería hacer en un ambiente de producción, se hizo solo con motivos de mostrar la funcionalidad. Insertamos la data con una mayor facilidad utilizando APPEND_VALUES Hint, un utilitario de Oracle que nos permite insertar data con mayor performance.
INSERT /*+ APPEND */ INTO lineas_de_pedido SELECT level AS id, TRUNC(DBMS_RANDOM.value(1,1000)) AS id_orden, TRUNC(DBMS_RANDOM.value(1,20)) AS cantidad_linea, ROUND(DBMS_RANDOM.value(1,1000),2) AS total, TRUNC(SYSDATE - DBMS_RANDOM.value(0,366)) AS fecha_creacion FROM dual CONNECT BY level <= 100000; COMMIT;
Ejecutamos el siguiente query, motivo para que Oracle pueda recopilar estadísticas de la tabla:
EXEC DBMS_STATS.gather_table_stats(USER, 'lineas_de_pedido');
Creando los logs de la Vista Materializada(Materialized View Logs): Dado que un refresh implica truncar el segmento de la vista materializada y volver a llenarlo mediante la consulta relacionada, trae consigo bastante tiempo así como procesamiento, sobre todo cuando se realiza una consulta a una tabla remota. Con el motivo de reducir los costos de replicación, crearemos los logs de la vista materializada para capturar todos los cambios en la tabla desde el último refresh. Esto nos permite una actualización rápida, en donde solo necesitaremos aplicar los cambios en lugar de actualizar por completo la vista materializada. Mediante el siquiente query crearemos el Materialized view logs para poder hacer el tracking de cualquier cambio DML en la tabla base:
CREATE MATERIALIZED VIEW LOG ON lineas_de_pedido WITH ROWID, SEQUENCE(id_orden, cantidad_linea, total) INCLUDING NEW VALUES;
Creando la Vista Materializada(Materialized View): Creamos la vista materializada, la cual hace algunas agregaciones como un parte del group by. Al poner ENABLE ON QUERY COMPUTATION le decimos a Oracle que esta es una real-time materialized view.
CREATE MATERIALIZED VIEW resumen_linea_rtmv REFRESH FAST ON DEMAND ENABLE QUERY REWRITE ENABLE ON QUERY COMPUTATION AS SELECT id_orden, SUM(cantidad_linea) AS sum_cantidades_lineas, SUM(total) AS sum_totales, COUNT(*) AS cantidad_filas FROM lineas_de_pedido GROUP BY id_orden;
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 3pyvm3rg8xaz8, child number 0
-------------------------------------
SELECT id_orden, SUM(cantidad_linea) AS sum_cantidades_lineas,
SUM(total) AS sum_totales, COUNT(*) AS cantidad_filas FROM
lineas_de_pedido WHERE id_orden = 1 GROUP BY id_orden
Plan hash value: 784120407
---------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 4 (100)| |
|* 1 | MAT_VIEW REWRITE ACCESS FULL| RESUMEN_LINEA_RTMV | 1 | 18 | 4 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("RESUMEN_LINEA_RTMV"."ID_ORDEN"=1)
20 rows selected.
Podemos ver en el plan de ejecución que se utilizó la vista materializada, en lugar de acceder a la tabla base. Observamos el valor de cantidad_filas de 92. A su vez, podemos darnos cuenta que Oracle hizo una reescritura del query para poder usar la vista materializada(MAT_VIEW REWRITE ACCESS FULL)
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 3pyvm3rg8xaz8, child number 1
-------------------------------------
SELECT id_orden, SUM(cantidad_linea) AS sum_cantidades_lineas,
SUM(total) AS sum_totales, COUNT(*) AS cantidad_filas FROM
lineas_de_pedido WHERE id_orden = 1 GROUP BY id_orden
Plan hash value: 1973604023
------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 19 (100)| |
| 1 | VIEW | | 3 | 156 | 19 (27)| 00:00:01 |
| 2 | UNION-ALL | | | | | |
|* 3 | FILTER | | | | | |
|* 4 | HASH JOIN OUTER | | 1 | 38 | 9 (23)| 00:00:01 |
|* 5 | MAT_VIEW ACCESS FULL | RESUMEN_LINEA_RTMV | 1 | 22 | 4 (0)| 00:00:01 |
| 6 | VIEW | | 1 | 16 | 5 (40)| 00:00:01 |
| 7 | HASH GROUP BY | | 1 | 39 | 5 (40)| 00:00:01 |
| 8 | VIEW | | 1 | 39 | 4 (25)| 00:00:01 |
| 9 | RESULT CACHE | 11vzug5w8mr6d6m7dv6chv3u29 | | | | |
|* 10 | VIEW | | 1 | 103 | 4 (25)| 00:00:01 |
| 11 | WINDOW SORT | | 1 | 194 | 4 (25)| 00:00:01 |
|* 12 | TABLE ACCESS FULL | MLOG$_LINEAS_DE_PEDIDO | 1 | 194 | 3 (0)| 00:00:01 |
| 13 | VIEW | | 2 | 104 | 10 (30)| 00:00:01 |
| 14 | UNION-ALL | | | | | |
|* 15 | FILTER | | | | | |
| 16 | NESTED LOOPS OUTER | | 1 | 95 | 4 (25)| 00:00:01 |
| 17 | VIEW | | 1 | 78 | 4 (25)| 00:00:01 |
|* 18 | FILTER | | | | | |
| 19 | HASH GROUP BY | | 1 | 39 | 4 (25)| 00:00:01 |
|* 20 | VIEW | | 1 | 39 | 4 (25)| 00:00:01 |
| 21 | RESULT CACHE | 11vzug5w8mr6d6m7dv6chv3u29 | | | | |
|* 22 | VIEW | | 1 | 103 | 4 (25)| 00:00:01 |
| 23 | WINDOW SORT | | 1 | 194 | 4 (25)| 00:00:01 |
|* 24 | TABLE ACCESS FULL | MLOG$_LINEAS_DE_PEDIDO | 1 | 194 | 3 (0)| 00:00:01 |
|* 25 | INDEX UNIQUE SCAN | I_SNAP$_RESUMEN_LINEA_RTMV | 1 | 17 | 0 (0)| |
| 26 | NESTED LOOPS | | 1 | 103 | 6 (34)| 00:00:01 |
| 27 | VIEW | | 1 | 81 | 5 (40)| 00:00:01 |
| 28 | HASH GROUP BY | | 1 | 39 | 5 (40)| 00:00:01 |
| 29 | VIEW | | 1 | 39 | 4 (25)| 00:00:01 |
| 30 | RESULT CACHE | 11vzug5w8mr6d6m7dv6chv3u29 | | | | |
|* 31 | VIEW | | 1 | 103 | 4 (25)| 00:00:01 |
| 32 | WINDOW SORT | | 1 | 194 | 4 (25)| 00:00:01 |
|* 33 | TABLE ACCESS FULL | MLOG$_LINEAS_DE_PEDIDO | 1 | 194 | 3 (0)| 00:00:01 |
|* 34 | MAT_VIEW ACCESS BY INDEX ROWID| RESUMEN_LINEA_RTMV | 1 | 22 | 1 (0)| 00:00:01 |
|* 35 | INDEX UNIQUE SCAN | I_SNAP$_RESUMEN_LINEA_RTMV | 1 | | 0 (0)| |
------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("AV$0"."OJ_MARK" IS NULL)
4 - access(SYS_OP_MAP_NONNULL("ID_ORDEN")=SYS_OP_MAP_NONNULL("AV$0"."GB0"))
5 - filter("RESUMEN_LINEA_RTMV"."ID_ORDEN"=1)
10 - filter((("MAS$"."OLD_NEW$$"='N' AND "MAS$"."SEQ$$"="MAS$"."MAXSEQ$$") OR
(INTERNAL_FUNCTION("MAS$"."OLD_NEW$$") AND "MAS$"."SEQ$$"="MAS$"."MINSEQ$$")))
12 - filter("MAS$"."SNAPTIME$$">TO_DATE(' 2020-07-07 16:47:48', 'syyyy-mm-dd hh24:mi:ss'))
15 - filter(CASE WHEN ROWID IS NOT NULL THEN 1 ELSE NULL END IS NULL)
18 - filter(SUM(1)>0)
20 - filter("MAS$"."ID_ORDEN"=1)
22 - filter((("MAS$"."OLD_NEW$$"='N' AND "MAS$"."SEQ$$"="MAS$"."MAXSEQ$$") OR
(INTERNAL_FUNCTION("MAS$"."OLD_NEW$$") AND "MAS$"."SEQ$$"="MAS$"."MINSEQ$$")))
24 - filter("MAS$"."SNAPTIME$$">TO_DATE(' 2020-07-07 16:47:48', 'syyyy-mm-dd hh24:mi:ss'))
25 - access("RESUMEN_LINEA_RTMV"."SYS_NC00005$"=SYS_OP_MAP_NONNULL("AV$0"."GB0"))
31 - filter((("MAS$"."OLD_NEW$$"='N' AND "MAS$"."SEQ$$"="MAS$"."MAXSEQ$$") OR
(INTERNAL_FUNCTION("MAS$"."OLD_NEW$$") AND "MAS$"."SEQ$$"="MAS$"."MINSEQ$$")))
33 - filter("MAS$"."SNAPTIME$$">TO_DATE(' 2020-07-07 16:47:48', 'syyyy-mm-dd hh24:mi:ss'))
34 - filter(("RESUMEN_LINEA_RTMV"."ID_ORDEN"=1 AND "RESUMEN_LINEA_RTMV"."CANTIDAD_FILAS"+"AV$0"."D0">0))
35 - access("RESUMEN_LINEA_RTMV"."SYS_NC00005$"=SYS_OP_MAP_NONNULL("AV$0"."GB0"))
Result Cache Information (identified by operation id):
------------------------------------------------------
9 -
21 -
30 -
Note
-----
- dynamic statistics used: dynamic sampling (level=2)
82 rows selected.
Consulta directa a la vista materializada
Además de reescribir las consultas, también se puede consultar directamente a la vista materializada de forma directa. Al hacer la consulta, obtenemos el contenido actual de la vista materualizada por defecto:
SELECT id_orden,
sum_cantidades_lineas,
sum_totales,
cantidad_filas
FROM resumen_linea_rtmv
WHERE id_orden = 1;
Al hacer la consulta a la misma vista materializada, nos damos cuenta que la data sigue estando como antes de ser modificada. Nota: La vista de la data ha sido nivelada solo en nuestra sesión: La vista materializada no ha sido actualizada todavía. SET LINESIZE 200 PAGESIZE 100 SELECT * FROM dbms_xplan.display_cursor();
Consulta a la vista materializada con FRESH_MV:
Este utilitario le dice a Oracle que queremos aprovechar la funcionalidad en tiempo real cuando hacemos una consulta directa contr la vista materializada, por lo que vemos cantidad_filas aumentado a 93, paar ello, ejecutamos el siquiente query:
SELECT /*+ FRESH_MV */
id_orden,
sum_cantidades_lineas,
sum_totales,
cantidad_filas
FROM resumen_linea_rtmv
WHERE id_orden = 1;
Como podemos ver, podemos obtener la información actualizada consultando directamente la vista materializada mediante FRESH_MV.
SET LINESIZE 200 PAGESIZE 100
SELECT *
FROM dbms_xplan.display_cursor();
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 87f19u93pg8wq, child number 0
-------------------------------------
SELECT /*+ FRESH_MV */ id_orden, sum_cantidades_lineas,
sum_totales, cantidad_filas FROM resumen_linea_rtmv
WHERE id_orden = 1
Plan hash value: 1973604023
------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 19 (100)| |
| 1 | VIEW | | 3 | 156 | 19 (27)| 00:00:01 |
| 2 | UNION-ALL | | | | | |
|* 3 | FILTER | | | | | |
|* 4 | HASH JOIN OUTER | | 1 | 38 | 9 (23)| 00:00:01 |
|* 5 | MAT_VIEW ACCESS FULL | RESUMEN_LINEA_RTMV | 1 | 22 | 4 (0)| 00:00:01 |
| 6 | VIEW | | 1 | 16 | 5 (40)| 00:00:01 |
| 7 | HASH GROUP BY | | 1 | 39 | 5 (40)| 00:00:01 |
| 8 | VIEW | | 1 | 39 | 4 (25)| 00:00:01 |
| 9 | RESULT CACHE | 11vzug5w8mr6d6m7dv6chv3u29 | | | | |
|* 10 | VIEW | | 1 | 103 | 4 (25)| 00:00:01 |
| 11 | WINDOW SORT | | 1 | 194 | 4 (25)| 00:00:01 |
|* 12 | TABLE ACCESS FULL | MLOG$_LINEAS_DE_PEDIDO | 1 | 194 | 3 (0)| 00:00:01 |
| 13 | VIEW | | 2 | 104 | 10 (30)| 00:00:01 |
| 14 | UNION-ALL | | | | | |
|* 15 | FILTER | | | | | |
| 16 | NESTED LOOPS OUTER | | 1 | 95 | 4 (25)| 00:00:01 |
| 17 | VIEW | | 1 | 78 | 4 (25)| 00:00:01 |
|* 18 | FILTER | | | | | |
| 19 | HASH GROUP BY | | 1 | 39 | 4 (25)| 00:00:01 |
|* 20 | VIEW | | 1 | 39 | 4 (25)| 00:00:01 |
| 21 | RESULT CACHE | 11vzug5w8mr6d6m7dv6chv3u29 | | | | |
|* 22 | VIEW | | 1 | 103 | 4 (25)| 00:00:01 |
| 23 | WINDOW SORT | | 1 | 194 | 4 (25)| 00:00:01 |
|* 24 | TABLE ACCESS FULL | MLOG$_LINEAS_DE_PEDIDO | 1 | 194 | 3 (0)| 00:00:01 |
|* 25 | INDEX UNIQUE SCAN | I_SNAP$_RESUMEN_LINEA_RTMV | 1 | 17 | 0 (0)| |
| 26 | NESTED LOOPS | | 1 | 103 | 6 (34)| 00:00:01 |
| 27 | VIEW | | 1 | 81 | 5 (40)| 00:00:01 |
| 28 | HASH GROUP BY | | 1 | 39 | 5 (40)| 00:00:01 |
| 29 | VIEW | | 1 | 39 | 4 (25)| 00:00:01 |
| 30 | RESULT CACHE | 11vzug5w8mr6d6m7dv6chv3u29 | | | | |
|* 31 | VIEW | | 1 | 103 | 4 (25)| 00:00:01 |
| 32 | WINDOW SORT | | 1 | 194 | 4 (25)| 00:00:01 |
|* 33 | TABLE ACCESS FULL | MLOG$_LINEAS_DE_PEDIDO | 1 | 194 | 3 (0)| 00:00:01 |
|* 34 | MAT_VIEW ACCESS BY INDEX ROWID| RESUMEN_LINEA_RTMV | 1 | 22 | 1 (0)| 00:00:01 |
|* 35 | INDEX UNIQUE SCAN | I_SNAP$_RESUMEN_LINEA_RTMV | 1 | | 0 (0)| |
------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("AV$0"."OJ_MARK" IS NULL)
4 - access(SYS_OP_MAP_NONNULL("ID_ORDEN")=SYS_OP_MAP_NONNULL("AV$0"."GB0"))
5 - filter("RESUMEN_LINEA_RTMV"."ID_ORDEN"=1)
10 - filter((("MAS$"."OLD_NEW$$"='N' AND "MAS$"."SEQ$$"="MAS$"."MAXSEQ$$") OR
(INTERNAL_FUNCTION("MAS$"."OLD_NEW$$") AND "MAS$"."SEQ$$"="MAS$"."MINSEQ$$")))
12 - filter("MAS$"."SNAPTIME$$">TO_DATE(' 2020-07-07 16:47:48', 'syyyy-mm-dd hh24:mi:ss'))
15 - filter(CASE WHEN ROWID IS NOT NULL THEN 1 ELSE NULL END IS NULL)
18 - filter(SUM(1)>0)
20 - filter("MAS$"."ID_ORDEN"=1)
22 - filter((("MAS$"."OLD_NEW$$"='N' AND "MAS$"."SEQ$$"="MAS$"."MAXSEQ$$") OR
(INTERNAL_FUNCTION("MAS$"."OLD_NEW$$") AND "MAS$"."SEQ$$"="MAS$"."MINSEQ$$")))
24 - filter("MAS$"."SNAPTIME$$">TO_DATE(' 2020-07-07 16:47:48', 'syyyy-mm-dd hh24:mi:ss'))
25 - access("RESUMEN_LINEA_RTMV"."SYS_NC00005$"=SYS_OP_MAP_NONNULL("AV$0"."GB0"))
31 - filter((("MAS$"."OLD_NEW$$"='N' AND "MAS$"."SEQ$$"="MAS$"."MAXSEQ$$") OR
(INTERNAL_FUNCTION("MAS$"."OLD_NEW$$") AND "MAS$"."SEQ$$"="MAS$"."MINSEQ$$")))
33 - filter("MAS$"."SNAPTIME$$">TO_DATE(' 2020-07-07 16:47:48', 'syyyy-mm-dd hh24:mi:ss'))
34 - filter(("RESUMEN_LINEA_RTMV"."ID_ORDEN"=1 AND "RESUMEN_LINEA_RTMV"."CANTIDAD_FILAS"+"AV$0"."D0">0))
35 - access("RESUMEN_LINEA_RTMV"."SYS_NC00005$"=SYS_OP_MAP_NONNULL("AV$0"."GB0"))
Result Cache Information (identified by operation id):
------------------------------------------------------
9 -
21 -
30 -
Note
-----
- dynamic statistics used: dynamic sampling (level=2)
82 rows selected.
- https://docs.oracle.com/en/database/oracle/oracle-database/12.2/dwhsg/advanced-materialized-views.html#GUID-0702359B-D379-4299-86C4-2958BCD4381D
- https://docs.oracle.com/cd/E11882_01/server.112/e41084/statements_6002.htm#SQLRF01302
- https://docs.oracle.com/cd/E11882_01/server.112/e41084/statements_6003.htm#SQLRF01303
Comentarios
Publicar un comentario