El año 0 y Oracle GoldenGate

El año 0 no existe. Del año -1 (es decir, año 1 antes de Cristo) se pasa al año 1 y el primer año bisiesto de la historia es el año 4.
No obstante, en una base de datos he visto filas que en un campo de tipo DATE habían conseguido introducir una fecha ’29-FEB-0000′.
Oracle realiza dos controles con las fechas:
Uno específicamente sobre el año cero, que es el siguiente:
SQL> create table fechas (fecha date);

Tabla creada.

SQL> insert into fechas values (to_date(’01-01-0000′,’DD-MM-YYYY’));
insert into fechas values (to_date(’01-01-0000′,’DD-MM-YYYY’))
                                    *
ERROR en línea 1:
ORA-01841: el valor (completo) del año debe estar entre -4713 y +9999, y no debe ser igual a 0
Otro sobre la validez de la fecha, de modo que:

  • Un día 31 es válido sólo para los meses enero, marzo, mayo, julio, agosto, octubre y diciembre.
  • Un día 29 sólo es válido para el mes de febrero en los años bisiestos.
  • Un día 30 es válido para todos los meses excepto febrero.

SQL> insert into fechas values (to_date(’29-02-0001′,’DD-MM-YYYY’));
insert into fechas values (to_date(’29-02-0001′,’DD-MM-YYYY’))
                                    *
ERROR en línea 1:
ORA-01839: fecha incorrecta para el mes especificado
Como comento en este caso, en una base de datos consiguieron saltar los controles del motor y se introdujeron fechas en año 0, incluso el ’29-FEB-0000′.
NOTA: Ni idea cómo lo consiguieron. Intenté insertar esa fecha con SQL dinámico, o desde PL/SQL, etc y siempre recibía alguno de estos dos errores.
SQL>  select CODIGO, FECHA from  TABLA_BASE
  2  where FECHA =(select min(FECHA) from TABLA_BASE);

CODIGO       FECHA
———— ——————–
000600211048 01-ENE-0000 00:00:00
00060164681- 01-ENE-0000 00:00:00
El caso es que esa base de datos se iba a migrar de Oracle9i a Oracle11gR2, y los procedimientos de export/import propagaban las fechas incluyendo estos registros sobre el año 0. De modo que la importación fue bien, pero en cuanto volvieron a insertar un año 0 en la tabla, al propagarse por Orace GoldenGate al futuro entorno, el error ORA-01841 apareció de pronto.
Oracle GoldenGate Delivery for Oracle process started, group REPL discard file opened: 2013-09-26 17:07:02

Current time: 2013-09-26 17:16:35
Discarded record from action ABEND on error 1841

OCI Error ORA-01841: el valor (completo) del año debe estar entre -4713 y +9999, y no debe ser igual a 0 (status = 1841). UPDATE «APP_OWNER».»TABLA_BASE» SET «ACTIVO» = :a4,»TIPO» = :a5,»DURACION» = :a6,»FECHA» = :a7 WHERE «CODIGO» = :b0 AND «FECHA_ORIGEN» = :b1 AND «CATEGORIA» = :b2 AND «TABLA_BASE_CAT» = :b3

Aborting transaction on ./dirdat/ab beginning at seqno 58 rba 4270475
                         error at seqno 58 rba 4270475
Problem replicating APP_OWNER.TABLA_BASE to APP_OWNER.TABLA_BASE
Mapping problem with compressed update record (target format)…
*
CODIGO = 000600371922
FECHA_ORIGEN = 2004-05-27 00:00:00
CATEGORIA = asi1
TABLA_BASE_CAT = ~#
ACTIVO = NULL
TIPO = T
DURACION = 60
FECHA = 0000-02-29 00:00:00
*

Process Abending : 2013-09-26 17:16:35
Para solucionar esto, en el fichero de parámetros de REPLICAT, hay que mapear las fechas a una fecha válida. Para evitar, además, el problema del 29 de febrero envié el mapeo al año 0004.
Replicat repl
UserID oggadm1@bbdd, password *****
AssumeTargetDefs
DiscardFile ./dirrpt/repl.dsc
ALLOWNOOPUPDATES
TABLEEXCLUDE APP_OWNER.VM_TABLE1_DS
TABLEEXCLUDE APP_OWNER.VM_TABLE2_DS
Map app_owner.tabla_base, Target app_owner.tabla_base,
COLMAP (USEDEFAULTS,fecha=@STRSUB(fecha,»0000″,»0004″));
Map app_owner.*, Target app_owner.*;

Optimización SQL en Oracle – Ya a la venta!

El libro ya está disponible en todas las tiendas Amazon (.com, .co.uk, .es, .de, etc.)

¡Muchísimas gracias! ¡Espero que os guste y os sea útil!

Amazon.es              Amazon.com

Podéis echarle un ojo al interior aquí (mejor en pantalla completa):

Optimización SQL en Oracle. Últimos retoques.

¡Por fin!

El libro «Optimización SQL en Oracle» está terminado. 

En cuanto finalice el diseño de la portada y la contraportada (si los de Amazon no ponen impedimento) ya estará disponible para comprar tanto en amazon.com como en amazon.eu.

El libro ha contado con dos revisores técnicos de peso, que le han sacado punta a todo y han sentado a debate tanto los ejemplos, imágenes y conceptos expuestos, como la filosofía de trabajo del libro. Son Arturo Gutierrez y Jetro Marco. Gracias a ellos el libro ha pasado de unas 316 páginas a las más de 420 actuales. 
El índice finalmente ha quedado así:
Al final abordamos todo desde las tripas, tanto el tratamiento de las bind variables como el funcionamiento del Bind Variable Peeking, el Adaptive Cursor Sharing, el  como SQL Profiles, SQL Baselines, reescritura de vistas materializadas, el paralelismo, particionamiento, así como trazas y planes de ejecución de todo.
El libro está lleno de técnicas, herramientas, base de conocimiento y nuestro aporte profesional sobre cómo optimizar cualquier SQL que de un mal rendimiento. Nos metemos a contar qué pasa en particular con los entornos data warehouse (¿Se puede ejecutar PL/SQL masivo en un entorno data warehouse? … pues hay un capítulo que trata de cómo hacerlo a un rendimiento brutal!), desmontar mitos, descubrir la realizad subyacente de trucos como lanzar INSERTS con APPEND y cosas así. ¿Por qué Oracle dice que seguirá un plan de ejecución y luego decide utilizar otro? ¿cómo lidiar con todo esto sin volvernos locos????
Hemos destripado todas las hints, poniéndolas a prueba. Hemos buscado ejemplos de SQL ineficiente y de múltiples ejecuciones de un mismo código para rizar el rizo y comprender qué sucede en el CBO, cómo se estima la cardinalidad de las operaciones, por qué (a veces) Oracle se equivoca y por qué a veces somos nosotros los que no entendemos al motor.
Además, la bbdd está disponible para descargar gratuitamente aquí, y el SQL del libro (próximamente)!

Manejo de subconsultas en la cláusula SELECT. Parte II

(Continúa de Parte I)

Este post podría llamarse «La paradoja del increíble coste menguante» como si de un relato de G. K. Chesterton se tratara.

Si alguien pensó por la lectura de la parte I de este post que las subconsultas en la cláusula SELECT mejoraban el rendimiento, pues permitían reproducir consultas en estrella sin necesidad de tener un modelo en estrella, ni dimensiones ni jerarquías, está al borde de cometer un grave error.
El optimizador ignora los costes de combinación de las subconsultas en la cláusula SELECT, contando únicamente con el coste de acceso a los objetos de esa subconsulta. Esto sucede incluso en versión Oracle11gR2.
Como ejemplo sirva la siguiente consulta formulada sobre VUELOS (57.711 filas), RESERVAS (171.113 filas) y CLIENTES (9999 filas).

Consulta de reservas, con datos de vuelos y clientes expresado con dos joins

select reservas.id_reserva, reservas.importe, vuelos.detalles, clientes.apellidos
    from vuelos, reservas, clientes
    where vuelos.id_vuelo=reservas.vue_id_vuelo
      and reservas.cli_nif=clientes.nif;

Consulta de reservas, con datos de vuelos y clientes expresado con una join y una subconsulta en la cláusula SELECT

select reservas.id_reserva, reservas.importe,
     (select vuelos.detalles from vuelos
       where vuelos.id_vuelo=reservas.vue_id_vuelo) vuelo,
    clientes.apellidos
    from reservas, clientes
    where reservas.cli_nif=clientes.nif;

Consulta de reservas, con datos de vuelos y clientes expresado con dos subconsultas en la cláusula SELECT

select reservas.id_reserva, reservas.importe,
     (select vuelos.detalles from vuelos   
       where reservas.vue_id_vuelo=vuelos.id_vuelo) vuelo,
     (select clientes.apellidos    from clientes 
       where reservas.cli_nif=clientes.nif) cliente
    from reservas;
Los correspondientes planes de ejecución parecen evidenciar lo mencionado anteriormente: el optimizador de costes no es capaz de evaluar el impacto de la combinación de elementos de la consulta principal con los de las subconsultas en la cláusula SELECT. Por este motivo, los costes de los planes de ejecución cada vez son inferiores.

Ejecución de la consulta de reservas, con datos de vuelos y clientes expresado con dos joins con el plan de ejecución asociado y la traza de AUTOTRACE

SQL> select reservas.id_reserva, reservas.importe, vuelos.detalles, clientes.apellidos
  2      from vuelos, reservas, clientes
  3      where vuelos.id_vuelo=reservas.vue_id_vuelo
  4        and reservas.cli_nif=clientes.nif;
171113 filas seleccionadas.
Transcurrido: 00:00:01.54
Plan de Ejecución
———————————————————-
Plan hash value: 858327892
—————————————————————————————-
| Id  | Operation           | Name     | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
—————————————————————————————-
|   0 | SELECT STATEMENT    |          |   171K|    13M|       |   904   (2)| 00:00:11 |
|*  1 |  HASH JOIN          |          |   171K|    13M|       |   904   (2)| 00:00:11 |
|   2 |   TABLE ACCESS FULL | CLIENTES |  9999 |   361K|       |    27   (0)| 00:00:01 |
|*  3 |   HASH JOIN         |          |   171K|  7686K|  1528K|   875   (2)| 00:00:11 |
|   4 |    TABLE ACCESS FULL| VUELOS   | 57711 |   845K|       |   137   (1)| 00:00:02 |
|   5 |    TABLE ACCESS FULL| RESERVAS |   171K|  5180K|       |   311   (2)| 00:00:04 |
—————————————————————————————-
Predicate Information (identified by operation id):
—————————————————
   1 – access(«RESERVAS».»CLI_NIF»=»CLIENTES».»NIF»)
   3 – access(«VUELOS».»ID_VUELO»=»RESERVAS».»VUE_ID_VUELO»)
Estadísticas
———————————————————-
         15  recursive calls
          0  db block gets
      13013  consistent gets
         96  physical reads
          0  redo size
    7835592  bytes sent via SQL*Net to client
     125996  bytes received via SQL*Net from client
      11409  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
     171113  rows processed
A grandes rasgos, el resumen de la ejecución puede ser una lectura de 13.013 bloques en memoria, un tiempo de ejecución de 1 minuto y 54 segundos y un coste de 904.

Ejecución de la consulta de reservas, con datos de vuelos y clientes expresado con una join y una subconsulta en la cláusula SELECT con el plan de ejecución asociado y la traza de AUTOTRACE

SQL> select reservas.id_reserva, reservas.importe,
  2     (select vuelos.detalles from vuelos
  3         where vuelos.id_vuelo=reservas.vue_id_vuelo) vuelo,
  4      clientes.apellidos
  5      from reservas, clientes
  6      where reservas.cli_nif=clientes.nif;
171113 filas seleccionadas.
Transcurrido: 00:00:02.40
Plan de Ejecución
———————————————————-
Plan hash value: 402988295
—————————————————————————————-
| Id  | Operation                   | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
—————————————————————————————-
|   0 | SELECT STATEMENT            |          |   171K|    11M|   340   (2)| 00:00:05 |
|   1 |  TABLE ACCESS BY INDEX ROWID| VUELOS   |     1 |    15 |     2   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | VUE_PK   |     1 |       |     1   (0)| 00:00:01 |
|*  3 |  HASH JOIN                  |          |   171K|    11M|   340   (2)| 00:00:05 |
|   4 |   TABLE ACCESS FULL         | CLIENTES |  9999 |   361K|    27   (0)| 00:00:01 |
|   5 |   TABLE ACCESS FULL         | RESERVAS |   171K|  5180K|   311   (2)| 00:00:04 |
—————————————————————————————-
Predicate Information (identified by operation id):
—————————————————
   2 – access(«VUELOS».»ID_VUELO»=:B1)
   3 – access(«RESERVAS».»CLI_NIF»=»CLIENTES».»NIF»)
Estadísticas
———————————————————-
         15  recursive calls
          0  db block gets
     374003  consistent gets
          0  physical reads
          0  redo size
    7835589  bytes sent via SQL*Net to client
     125996  bytes received via SQL*Net from client
      11409  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
     171113  rows processed
En esta ejecución, el número de bloques leídos en memoria ha aumentado a 374.003 y el tiempo de ejecución ha aumentado a 2 minutos 40 segundos. Sin embargo el coste de la ejecución se ha reducido a 340 (menos de la mitad). El número de bytes estimado como total de la ejecución también se estima mejorado: de 13 millones a 11 millones.

Ejecución de la consulta de reservas, con datos de vuelos y clientes expresado con dos subconsultas en la cláusula SELECT con el plan de ejecución asociado y la traza de AUTOTRACE
SQL> select reservas.id_reserva, reservas.importe,
  2       (select vuelos.detalles from vuelos   
  3          where reservas.vue_id_vuelo=vuelos.id_vuelo) vuelo,
  4       (select clientes.apellidos    from clientes 
  5          where reservas.cli_nif=clientes.nif) cliente
  6      from reservas;
171113 filas seleccionadas.
Transcurrido: 00:00:02.39
Plan de Ejecución
———————————————————-
Plan hash value: 465102819
—————————————————————————————-
| Id  | Operation                   | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
—————————————————————————————-
|   0 | SELECT STATEMENT            |          |   171K|  5180K|   311   (2)| 00:00:04 |
|   1 |  TABLE ACCESS BY INDEX ROWID| VUELOS   |     1 |    15 |     2   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | VUE_PK   |     1 |       |     1   (0)| 00:00:01 |
|   3 |  TABLE ACCESS BY INDEX ROWID| CLIENTES |     1 |    37 |     2   (0)| 00:00:01 |
|*  4 |   INDEX UNIQUE SCAN         | CLI_PK   |     1 |       |     1   (0)| 00:00:01 |
|   5 |  TABLE ACCESS FULL          | RESERVAS |   171K|  5180K|   311   (2)| 00:00:04 |
—————————————————————————————-
Predicate Information (identified by operation id):
—————————————————
   2 – access(«VUELOS».»ID_VUELO»=:B1)
   4 – access(«CLIENTES».»NIF»=:B1)
Estadísticas
———————————————————-
         15  recursive calls
          0  db block gets
     406374  consistent gets
          0  physical reads
          0  redo size
    7835587  bytes sent via SQL*Net to client
     125996  bytes received via SQL*Net from client
      11409  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
     171113  rows processed
En este caso, el tiempo de ejecución es prácticamente el mismo, mientras que el coste se muestra aun mejor que el de la ejecución anterior (340 anteriores frente a 311) pero el número de bloques leídos en memoria aumenta (374.003 anteriores frente a 406.374).

Las trazas generadas por la utilidad tkprof vienen a confirmar prácticamente lo mostrado en la traza de autotrace.

Traza de la utilidad TKPROF sobre la consulta de reservas, con datos de vuelos y clientes expresada con dos joins

select reservas.id_reserva, reservas.importe, vuelos.detalles, clientes.apellidos
    from vuelos, reservas, clientes
    where vuelos.id_vuelo=reservas.vue_id_vuelo
      and reservas.cli_nif=clientes.nif
call     count       cpu    elapsed       disk      query    current        rows
——- ——  ——– ———- ———- ———- ———-  ———-
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch    11409      0.39       0.51         96      13009          0      171113
——- ——  ——– ———- ———- ———- ———-  ———-
total    11411      0.39       0.52         96      13009          0      171113

Traza de la utilidad TKPROF sobre la consulta de reservas, con datos de vuelos y clientes expresada con una join y una subconsulta en la cláusula SELECT

select reservas.id_reserva, reservas.importe,
     (select vuelos.detalles from vuelos where vuelos.id_vuelo=reservas.vue_id_vuelo) vuelo,
    clientes.apellidos
    from reservas, clientes
    where reservas.cli_nif=clientes.nif
call     count       cpu    elapsed       disk      query    current        rows
——- ——  ——– ———- ———- ———- ———-  ———-
Parse        1      0.01       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch    11409      1.26       1.27          0     373999          0      171113
——- ——  ——– ———- ———- ———- ———-  ———-
total    11411      1.27       1.27          0     373999          0      171113

Traza de la utilidad TKPROF sobre la consulta de reservas, con datos de vuelos y clientes expresada con dos subconsultas en la cláusula SELECT

select reservas.id_reserva, reservas.importe,
     (select vuelos.detalles from vuelos   
       where reservas.vue_id_vuelo=vuelos.id_vuelo) vuelo,
     (select clientes.apellidos    from clientes 
       where reservas.cli_nif=clientes.nif) cliente
    from reservas
call     count       cpu    elapsed       disk      query    current        rows
——- ——  ——– ———- ———- ———- ———-  ———-
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch    11409      1.32       1.24          0     406370          0      171113
——- ——  ——– ———- ———- ———- ———-  ———-
total    11411      1.32       1.24          0     406370          0      171113
En las dos ejecuciones con subconsultas en la cláusula SELECT se aprecia, además, el aumento de tiempo de CPU por el mayor número de bloques a procesar en memoria.
Cuidado, por tanto, con las subconsultas expresadas a ese nivel de ejecución, pues el optimizador no evalua sus pesos correctamente, al quedar fuera del estudio de accesos y combinaciones entre tablas, mediante joins y filtros convencionales. Los resultados expresados por los planes de ejecución de su estimación en coste pueden confundir, ya que muestran costes mejores sobre ejecuciones claramente más ineficientes.

Manejo de subconsultas en la cláusula SELECT. Parte I.

El motor SQL de Oracle es muy flexible. Se permite el uso de subconsultas en la cláusula WHERE, y HAVING, así como directamente en la cláusula FROM (se tratan como vistas) y también como forma de resolver una columna en el conjunto de resultados en la cláusula SELECT.
Lo curioso es que, dado que el formateo de filas se hace al final, de algún modo el optimizador ignora el coste de combinación de las tablas de la subconsulta, pues las trata una vez ha procesado la sentencia.
Por ejemplo, la consulta de clientes con su identificación de sexo y estado civil.
SQL> set autotrace traceonly
SQL> select nif, cn_sexo sexo, cn_ec estado
  2  from clientes, sexos, estadosciviles
  3  where clientes.ec_id_ec=estadosciviles.id_ec
  4    and clientes.sex_id_sexo=sexos.id_sexo;
9999 filas seleccionadas.
Plan de Ejecución
———————————————————-
Plan hash value: 3779357754
————————————————————————————————
| Id  | Operation                     | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
————————————————————————————————
|   0 | SELECT STATEMENT              |                | 10001 |   341K|    34   (6)| 00:00:01 |
|*  1 |  HASH JOIN                    |                | 10001 |   341K|    34   (6)| 00:00:01 |
|   2 |   TABLE ACCESS FULL           | ESTADOSCIVILES |     5 |    55 |     3   (0)| 00:00:01 |
|   3 |   MERGE JOIN                  |                | 10000 |   234K|    31   (7)| 00:00:01 |
|   4 |    TABLE ACCESS BY INDEX ROWID| SEXOS          |     2 |    16 |     2   (0)| 00:00:01 |
|   5 |     INDEX FULL SCAN           | SEX_PK         |     2 |       |     1   (0)| 00:00:01 |
|*  6 |    SORT JOIN                  |                |  9999 |   156K|    29   (7)| 00:00:01 |
|   7 |     TABLE ACCESS FULL         | CLIENTES       |  9999 |   156K|    27   (0)| 00:00:01 |
————————————————————————————————
Predicate Information (identified by operation id):
—————————————————
   1 – access(«CLIENTES».»EC_ID_EC»=»ESTADOSCIVILES».»ID_EC»)
   6 – access(«CLIENTES».»SEX_ID_SEXO»=»SEXOS».»ID_SEXO»)
       filter(«CLIENTES».»SEX_ID_SEXO»=»SEXOS».»ID_SEXO»)
Estadísticas
———————————————————-
          1  recursive calls
          0  db block gets
        108  consistent gets
          1  physical reads
          0  redo size
     346739  bytes sent via SQL*Net to client
       7846  bytes received via SQL*Net from client
        668  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
       9999  rows processed
La lectura de 108 bloques en memoria corresponde mayoritariamente a los bloques de la tabla CLIENTES, al índice de clave primaria de la tabla SEXOS y al acceso completo por FULL SCAN de la tabla ESTADOSCIVILES. El coste se tasa en 34.
Esta misma consulta, expresando la resolución del valor de sexo y estado civil del cliente directamente en las columnas de resultado, suponen un plan de ejecución similar, pero tasado ligeramente inferior.
SQL> select nif,
  2         (select cn_sexo from sexos where id_sexo=clientes.sex_id_sexo) sexo,
  3         (select cn_ec from estadosciviles where id_ec=clientes.ec_id_ec) estado
  4  from clientes;
9999 filas seleccionadas.
Transcurrido: 00:00:00.07
Plan de Ejecución
———————————————————-
Plan hash value: 3745735041
———————————————————————————————-
| Id  | Operation                   | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
———————————————————————————————-
|   0 | SELECT STATEMENT            |                |  9999 |   156K|    27   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| SEXOS          |     1 |     8 |     1   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | SEX_PK         |     1 |       |     0   (0)| 00:00:01 |
|   3 |  TABLE ACCESS BY INDEX ROWID| ESTADOSCIVILES |     1 |    11 |     1   (0)| 00:00:01 |
|*  4 |   INDEX UNIQUE SCAN         | EC_PK          |     1 |       |     0   (0)| 00:00:01 |
|   5 |  TABLE ACCESS FULL          | CLIENTES       |  9999 |   156K|    27   (0)| 00:00:01 |
———————————————————————————————-
Predicate Information (identified by operation id):
—————————————————
   2 – access(«ID_SEXO»=:B1)
   4 – access(«ID_EC»=:B1)
Estadísticas
———————————————————-
          1  recursive calls
          0  db block gets
        766  consistent gets
          0  physical reads
          0  redo size
     380260  bytes sent via SQL*Net to client
       7846  bytes received via SQL*Net from client
        668  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       9999  rows processed
Prácticamente, los costes de acceso y gestión de la tabla SEXOS y ESTADOSCIVILES parecen omitirse en el cálculo (el anterior plan medía 34, menos 3 de acceso a cada tabla y 1 de acceso al índice), pero el volumen total de bloques leídos en memoria se multiplica por siete.

Traza de ejecución con uso de joins

select nif, cn_sexo sexo, cn_ec estado
from clientes, sexos, estadosciviles
where clientes.ec_id_ec=estadosciviles.id_ec
  and clientes.sex_id_sexo=sexos.id_sexo
call     count       cpu    elapsed       disk      query    current        rows
——- ——  ——– ———- ———- ———- ———-  ———-
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch      668      0.01       0.04          1        108          0        9999
——- ——  ——– ———- ———- ———- ———-  ———-
total      670      0.01       0.04          1        108          0        9999
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 91  (VUELOS)
Number of plan statistics captured: 1
Rows     Execution Plan
——-  —————————————————
      0  SELECT STATEMENT   MODE: ALL_ROWS
   9999   HASH JOIN
      5    TABLE ACCESS   MODE: ANALYZED (FULL) OF ‘ESTADOSCIVILES’  (TABLE)
   9999    MERGE JOIN
      2     TABLE ACCESS   MODE: ANALYZED (BY INDEX ROWID) OF ‘SEXOS’ (TABLE)
      2      INDEX   MODE: ANALYZED (FULL SCAN) OF ‘SEX_PK’ (INDEX (UNIQUE))
   9999     SORT (JOIN)
   9999      TABLE ACCESS   MODE: ANALYZED (FULL) OF ‘CLIENTES’ (TABLE)

Traza de ejecución con subconsultas en la cláusula SELECT

select nif,
       (select cn_sexo from sexos where id_sexo=clientes.sex_id_sexo) sexo,
       (select cn_ec from estadosciviles where id_ec=clientes.ec_id_ec) estado
from clientes
call     count       cpu    elapsed       disk      query    current        rows
——- ——  ——– ———- ———- ———- ———-  ———-
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch      668      0.00       0.01          0        766          0        9999
——- ——  ——– ———- ———- ———- ———-  ———-
total      670      0.00       0.01          0        766          0        9999
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 91  (VUELOS)
Number of plan statistics captured: 1
Rows     Execution Plan
——-  —————————————————
      0  SELECT STATEMENT   MODE: ALL_ROWS
      2   TABLE ACCESS   MODE: ANALYZED (BY INDEX ROWID) OF ‘SEXOS’ (TABLE)
      2    INDEX   MODE: ANALYZED (UNIQUE SCAN) OF ‘SEX_PK’ (INDEX (UNIQUE))
      5   TABLE ACCESS   MODE: ANALYZED (BY INDEX ROWID) OF ‘ESTADOSCIVILES’ (TABLE)
      5    INDEX   MODE: ANALYZED (UNIQUE SCAN) OF ‘EC_PK’ (INDEX (UNIQUE) )
   9999   TABLE ACCESS   MODE: ANALYZED (FULL) OF ‘CLIENTES’ (TABLE)

La ejecución mediante subconsultas parece indicar al optimizador que no es necesario ordenar la tabla CLIENTES para facilitar las joins, y que el obtener por cada fila desde memoria las filas correspondientes al sexo y al estado civil resulta óptimo.
Es algo muy parecido a una ejecución en estrella. La diferencia está en el cálculo de un hash para resolver los valores de sexo y estado civil, o el acceso directo a los bloques. Aunque la segunda ejecución parezca ejecutarse en menos tiempo, hay que tener en cuenta el alto consumo de bloques en memoria, cosa que no sucedería en una ejecución en estrella. 
En este caso, la ejecución con subconsultas en la cláusula SELECT sustituyendo las joins ha supuesto una mejora del rendimiento. En la parte II publicaré un caso completamente opuesto. Mientras que los costes en el plan de ejecución aparentemente van siendo cada vez menores, el rendimiento real de las ejecuciones resulta cada vez peor.

Migraciones con mínimo tiempo de parada con Oracle GoldenGate.

Últimamente estoy enfrentando migraciones que suponen cambios de plataforma, que los clientes utilizan para subir la base de datos de versión. Así pues, bases de datos en solaris versión Oracle10g (10.1 por ejemplo) que han de migrarse a un entorno Linux Oracle11g ó Oracle10g (versión 10.2).

Si la plataforma del sistema operativo fuera la misma (de solaris a solaris o de linux a linux) y la migración no implicase un cambio de versión, una de las opciones recomendadas es duplicar la base de datos, convertir el clon duplicado en base de datos standby, aplicar los logs en el momento de la puesta en producción y abrir la base de datos duplicada como principal.

Si hay subida de versión, en la máquina destino debe haber dos juegos de binarios: el mismo que el de origen, para realizar la restauración, y el juego de binarios de la nueva versión. Así, con el juego de binarios primero se realiza el duplicado y restauración, y con el segundo se abre la base de datos en modo «migrate» y se ejecutan los scripts de upgrade. Mientras dure el proceso la base de datos principal ha de estar parada y eso es un obstáculo.

Si bien las arquitecturas de sistema operativo son diferentes será necesario comprobar si el «endian» es distinto entre las dos plataformas. Existen dos tipos de endian, el pequeño y el grande. Entre plataformas de un mismo endian, basta con duplicar con RMAN la base de datos, pero si el endian es distinto (de little a big, o de big a little) requiere además convertir los ficheros y realizar la migración transportando tablespaces.

SQL> select * from v$transportable_platform
  2  order by endian_format;

PLATFORM_ID PLATFORM_NAME                            ENDIAN_FORMAT
———– —————————————- ————–
          3 HP-UX (64-bit)                           Big
          6 AIX-Based Systems (64-bit)               Big
         18 IBM Power Based Linux                    Big
          2 Solaris[tm] OE (64-bit)                  Big
          4 HP-UX IA (64-bit)                        Big
         16 Apple Mac OS                             Big
          1 Solaris[tm] OE (32-bit)                  Big
          9 IBM zSeries Based Linux                  Big
         17 Solaris Operating System (x86)           Little
         19 HP IA Open VMS                           Little
         20 Solaris Operating System (x86-64)        Little
         12 Microsoft Windows x86 64-bit             Little
         13 Linux x86 64-bit                         Little
          8 Microsoft Windows IA (64-bit)            Little
         21 Apple Mac OS (x86-64)                    Little
         11 Linux IA (64-bit)                        Little
          5 HP Tru64 UNIX                            Little
         10 Linux IA (32-bit)                        Little
          7 Microsoft Windows IA (32-bit)            Little
         15 HP Open VMS                              Little

20 filas seleccionadas.

Existen varias soluciones para hacer una migración de este tipo con mínimo tiempo de parada, como utilizar streams o utilizar un dataguard lógico, pero Oracle GoldenGate es definitivamente uno de los mejores métodos y más versátiles para migrar tanto bases de datos enteras, como esquemas, realizando además cambios de versión y de plataforma sincronizando lógicamente dos entornos.

Los pasos a seguir serían los siguientes, tomando nodo1 y nodo2 como los dos servidores, origen y destino.

1.- Descargar el software de Oracle GoldenGate de eDelivery   http://edelivery.oracle.com  y descomprimirlo en ambos servidores.
(el registro es gratuíto, y su descarga con fines no comerciales también!)

host1@oracle $ ./ggsci
Oracle GoldenGate Command Interpreter for Oracle
Version 11.2.1.0.1 OGGCORE_11.2.1.0.1_PLATFORMS_120423.0230_FBO
Solaris, sparc, 64bit (optimized), Oracle 10g on Apr 24 2012 09:06:57
Copyright (C) 1995, 2012, Oracle and/or its affiliates. All rights reserved.
GGSCI (host1) 1> create subdirs
Creating subdirectories under current directory /opt/oracle/product/OracleGG
Parameter files                /opt/oracle/product/OracleGG/dirprm: already exists
Report files                   /opt/oracle/product/OracleGG/dirrpt: created
Checkpoint files               /opt/oracle/product/OracleGG/dirchk: created
Process status files           /opt/oracle/product/OracleGG/dirpcs: created
SQL script files               /opt/oracle/product/OracleGG/dirsql: created
Database definitions files     /opt/oracle/product/OracleGG/dirdef: created
Extract data files             /opt/oracle/product/OracleGG/dirdat: created
Temporary files                /opt/oracle/product/OracleGG/dirtmp: created
Stdout files                   /opt/oracle/product/OracleGG/dirout: created

2.- Chequear que la base de datos origen está en modo ARCHIVELOG


ARCHIVE LOG LIST;
3.- Activar el suplemental logging en la base de datos.

SELECT force_logging, supplemental_log_data_min FROM v$database;
ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;
ALTER DATABASE FORCE LOGGING;
ALTER SYSTEM SWITCH LOGFILE;
4.-Crear el usuario GoldenGate en la base de datos origen.

CREATE USER oggadm1 IDENTIFIED BY “13c1sa”;
GRANT dba TO oggadm1;
5.-Comprobar la visibilidad entre las bases de datos (todas).

host1@oracle $ tnsping b_new
TNS Ping Utility for Solaris: Version 10.2.0.5.0 – Production on 30-APR-2013 12:23:25
Copyright (c) 1997,  2010, Oracle.  All rights reserved.
Used parameter files:
Used TNSNAMES adapter to resolve the alias
Attempting to contact (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = host2)(PORT = 1521))) (CONNECT_DATA = (SID = BBDD2)))
OK (320 msec)

6.-Configurar manager.

host1@oracle $ more GLOBALS
CheckpointTable oggadm1.oggchkp
host1@oracle $ more startup.oby
DBLogin UserID oggadm1@B, Password 13c1sa
Start Mgr
Info Mgr
Info CheckpointTable
Set Editor vi
GGSCI (host1) 1> Edit Param mgr
«/opt/oracle/product/OracleGG/dirprm/mgr.prm» [New file]
Port 15002
PurgeOldExtracts ./dirdat/*, UseCheckpoints
GGSCI (host1) 1> obey startup.oby
GGSCI (host1) 2> DBLogin UserID oggadm1@B, Password 13c1sa
Successfully logged into database.
GGSCI (host1) 3> Start Mgr
MGR is already running.
GGSCI (host1) 4> Info Mgr
Manager is running (IP port host1.15001).
GGSCI (host1) 5> Info CheckpointTable
No checkpoint table specified, using GLOBALS specification (oggad1.oggchkp)…
Checkpoint table oggad1.oggchkp does not exist.
GGSCI (host1) 6> Set Editor vi
7.-Crear la tabla de checkpoints.

GGSCI (host1) 7> Add CheckpointTable
No checkpoint table specified, using GLOBALS specification (oggadm1.oggchkp)…
Successfully created checkpoint table oggadm1.oggchkp.

8.- Crear el proceso EXTRACT

GGSCI (host1) 9> edit param extr
host1@oracle $ more /opt/oracle/product/OracleGG/dirprm/extr.prm
Extract extr
UserID oggadm1@B, Password 13c1sa
ExtTrail ./dirdat/aa
Table USUARIO1.*;
Table USUARIO2.*;
Table USUARIO3.*;
Table USUARIO4.*;

GGSCI (host1) 2> Add Extract extr, TranLog, Begin Now

EXTRACT added.
GGSCI (host1) 18> info all
Program     Status      Group       Lag at Chkpt  Time Since Chkpt
MANAGER     RUNNING
EXTRACT     STOPPED     EXTR        00:00:00      00:00:44

9.- Arrancar el proceso EXTRACT en el servidor origen. Una vez arrancado lanzar un log switch y un EXPORT DATA PUMP de la base de datos principal sobre la réplica con la opción FLASHBACK_SCN=xxxxxxx. En este caso es el 501308592, correspondiente al FIRST_CHANGE del último redolog online.
10.- Añadir trandata para tablas en el servidor origen.
Add TranData
Add TranData USUARIO1.*
Add TranData USUARIO2.*
Add TranData USUARIO3.*
Add TranData USUARIO4.*




11.- Configurar manager en servidor de replica (pasos 6 y 7)

12.- Configurar pump en servidor principal.

GGSCI (host1) 2> edit param pump
«/opt/oracle/product/OracleGG/dirprm/pump.prm» [New file]
Extract pump
RmtHost host2, MgrPort 15002, Compress
RmtTrail ./dirdat/ab
Passthru
Table USUARIO1.*;
Table USUARIO2.*;
Table USUARIO3.*;
Table USUARIO4.*;

«/opt/oracle/product/OracleGG/dirprm/pump.prm» [New file] 8 lines, 969 characters
GGSCI (host1) 3> Add Extract pump, ExtTrailSource ./dirdat/aa
EXTRACT added.
GGSCI (host1) 4> Add RmtTrail ./dirdat/ab, Extract pump, megabytes 5
RMTTRAIL added.
GGSCI (host1) 5> info all
Program     Status      Group       Lag at Chkpt  Time Since Chkpt
MANAGER     RUNNING
EXTRACT     RUNNING     EXTR        00:00:00      00:00:02
EXTRACT     STOPPED     PUMP        00:00:00      00:00:11


13.- Configurar replicat en servidor replica.

GGSCI (host2) 10> edit param repl
Replicat repl
UserID oggadm1@B, password 13c1sa
AssumeTargetDefs
DiscardFile ./dirrpt/repl.dsc
Map USUARIO1.*, Target USUARIO1.*;
Map USUARIO2.*, Target USUARIO2.*;
Map USUARIO3.*, Target USUARIO3.*;
Map USUARIO4.*, Target USUARIO4.*;

GGSCI (host2) 11> Add Replicat repl, ExtTrail ./dirdat/ab
REPLICAT added.
GGSCI (host2) 12> info all
Program     Status      Group       Lag at Chkpt  Time Since Chkpt
MANAGER     RUNNING
REPLICAT    STOPPED     REPL        00:00:00      00:00:06

14.- Arrancar pump en servidor principal.

GGSCI (host1) 7> start extract pump
Sending START request to MANAGER …
EXTRACT PUMP starting
GGSCI (host1) 8> info all
Program     Status      Group       Lag at Chkpt  Time Since Chkpt
MANAGER     RUNNING
EXTRACT     RUNNING     EXTR        00:00:00      00:00:08
EXTRACT     RUNNING     PUMP        00:00:00      00:13:38
GGSCI (host1) 9> info all
Program     Status      Group       Lag at Chkpt  Time Since Chkpt
MANAGER     RUNNING
EXTRACT     RUNNING     EXTR        00:00:00      00:00:08
EXTRACT     RUNNING     PUMP        00:00:00      00:00:09
14.- Importar el fichero de Export Data Pump generado en el paso 9.


15.- Arrancar replicat en replica al SCN de la exportación.

GGSCI (host2) 16> info all
Program     Status      Group       Lag at Chkpt  Time Since Chkpt
MANAGER     RUNNING
REPLICAT    STOPPED     REPL        00:00:00      00:06:11
GGSCI (host1) 7> Start Replicat, aftercsn 501308592
GGSCI (host2) 9> Start Replicat repl, aftercsn 501308592
Sending START request to MANAGER …
REPLICAT REPL starting
GGSCI (host2) 10> info all
Program     Status      Group       Lag at Chkpt  Time Since Chkpt
MANAGER     RUNNING
REPLICAT    RUNNING     REPL        00:00:00      00:00:06



Una vez las dos bases de datos están sincronizadas, toda la información del servidor principal se propaga a la base de datos réplica, que es idéntica en estructura a la base de datos principal. En el momento de la puesta en producción simplemente hay que cambiar los clientes para dirigirlos a la réplica.

Optimización SQL en Oracle. En venta, próximamente!

En breve estará a la venta mi libro «Optimización SQL en Oracle». 
Durante los últimos dos años he estado escribiendo este libro que resume, a mi modo de ver, todo lo que un administrador o programador debería conocer para optimizar código SQL.
En él describo cómo funciona el optimizador y cómo se comporta el servidor para escoger los mejores planes de ejecución, los aspectos a considerar para crear tablas  de diferentes tipos (tablas IOT, clusters, tablas particionadas, etc.) y lo mismo relativo a los índices. Herramientas para optimizar SQL, desde asesores a las herramientas «manuales» como explain plan, tkprof, autotrace, generación de trazas, análisis de AWR, etc.
Además, también dedico un apartado a los entornos datawarehouse, a optimización SQL de código ineficiente con casos prácticos resueltos, y un glosario completo de hints con ejemplos de su uso y «maluso», y sus consecuencias para el rendimiento.
Este libro responde preguntas y cuestiones habituales como el motivo por qué no siempre es eficiente acceder a las tablas usando índices, escenarios ineficientes, usos incorrectos de tipos de datos y sus consecuencias en la optimización, uso correcto del paralelismo, el particionamiento, las vistas materializadas, jerarquías, dimensiones, consecuencias de usar NOLOGGING, como tratar subconsultas, uso de IN y EXISTS, DISTINCT, ordenaciones, etc.
El esquema del libro es el siguiente:
Por el momento está en fase BETA, pendiente de revisión técnica. Para esta revisión cuento con dos administradores de los más fuertes de España, y vamos a asegurarnos que en las más de 300 páginas no se nos escapa un error.
Me gustaría decir, como los de Valve, «When it’s done, it’s done» como fecha de publicación, pero espero que en cosa de un par de meses pueda estar disponible a la venta.
Estoy contento porque se trata del primer libro en español que trata exclusivamente de optimización SQL y todo su universo. Muchos libros (principalmente en inglés) tratan de aspectos del rendimiento, sobre todo del motor (memoria, procesos) o se centran exclusivamente en administración o programación, pero éste es el primer libro que conozco absolutamente específico, en español, con ejemplos en español, tablas con nombres en cristiano (vuelos, reservas, etc.), sin ser una traducción de una obra en inglés o un copia/pega de partes de la documentación de Oracle.
Yo estoy satisfecho del resultado, y espero que pueda ser de utilidad. Estoy seguro de que incluso los usuarios más avanzados se sorprenderán aprendiendo cosas nuevas, o redefiniendo conceptos, o encontrando una forma práctica y accesible de resumir las funcionalidades y componentes que afectan a la eficiencia del servidor de base de datos.
Os dejo unas imágenes del libro, en fase BETA, listo para revisarlo y corregirlo antes de sacarlo a la luz.


El misterioso caso del error de «privilegios insuficientes» en la creación de vista materializada dentro de un procedimiento PL/SQL.

Recuerdo aquella vez que me pidieron crear un usuario para una aplicación en desarrollo. El usuario debía ser capaz de crear vistas y vistas materializadas, de modo que usé la siguiente sintaxis.

SQL> create user desarrollo identified by desarrollo;
User created.
SQL> grant connect, resource, create view, create materialized view to desarrollo;
Grant succeeded.

De este modo, creía yo, garantizaba que el usuario podría tener ese privilegio de forma explícita, y no mediante un rol, y así descartaba errores que podrían producirse por la no herencia de privilegios a través de roles en el uso de PL/SQL.

No obstante, el usuario vino a mi mesa a decirme: «No puedo crear vistas materializadas. Privilegios insuficientes».
 -¿Cómo es posible? – pregunté sorprendido. – Te aseguro que el usuario tiene privilegios para crearlas.

De modo que abrí una consola de sqlplus y ejecuté:

SQL> create materialized view test as select * from dual;
Materialized view created.

– ¿Ves? El usuario tiene privilegios.
– Ya, pero es que yo lo ejecuto dentro de un procedimiento PL/SQL, mediante el comando EXECUTE IMMEDIATE. – contestó.
– Vamos a probar.

SQL> drop materialized view test;
Materialized view dropped.
SQL> begin
  2      execute immediate ‘create materialized view test as select * from dual’;
  3  end;
  4  /
PL/SQL procedure successfully completed.
SQL> desc test
 Name                                      Null?    Type
 —————————————– ——– —————————-
 DUMMY                                              VARCHAR2(1)

– Pues no lo entiendo – argumentaba, sin salir de su asombro- mi procedimiento PL/SQL da error al ejecutarlo, y la sintaxis ejecutada en una consola de SQL no da errores.
¿qué podría ser? ¿por qué una vista materializada daba error en un procedimiento y no en un bloque anónimo?. Decidí hacer la prueba y comprobarlo por mi mismo. No podía creerlo.
SQL> drop materialized view test;
Materialized view dropped.
SQL> create procedure crea_mv_test as
  2  begin
  3      execute immediate ‘create materialized view test as select * from dual’;
  4  end;
  5  /
*
ERROR at line 1:
ORA-01031: insufficient privileges
ORA-06512: at «DESARROLLO.CREA_MV_TEST», line 3
ORA-06512: at line 1
El usuario tenía privilegios explícitos para crear una vista materializada, incluso desde un bloque anónimo, pero no tenía privilegios suficientes para crearla desde dentro de un procedimiento PL/SQL. 
No tenía sentido. El mismo usuario podía lanzar esa sintaxis desde la línea de comandos de sqlplus, dentro de un bloque anónimo, pero no en la creación de un procedimiento PL/SQL.
Entonces vi la respuesta, como una luz guiando mis dedos a añadir la cláusula authid current_user.
Así fue. La ejecución del código funcionaba cuando se especificaba claramente que los privilegios debían ser los del usuario conectado y no los del creador del procedimiento.
SQL> create or replace procedure crea_mv_test authid current_user as
  2  begin
  3         execute immediate ‘create materialized view test as select * from dual’;
  4  end;
  5  /
Procedure created.
SQL> exec crea_mv_test;
PL/SQL procedure successfully completed.
– Añade esta cláusula al procedure y vuelve a ejecutarlo.
– Funciona! – Exclamó. – ¿Cómo es posible?
– Porque la cláusula authid current_user está heredando los privilegios del usuario que ejecuta el procedimiento, y no los privilegios que tiene el usuario creador del procedimiento.
– Pero, ¡Si se trata del mismo usuario! ¿qué sentido tiene?
Yo ya no atendía a sus planteamientos. Había resuelto el problema. El código se compilaba y ejecutaba correctamente. Las vistas materializabas se creaban todas en sucesión, sin mostrar ningún error. Mi trabajo estaba hecho. Pasó el tiempo, y nunca sabré si lo sucedido aquel día respondía a, lo que se suele llamar en los círculos técnicos, un «expected behaviour».

Instalación Oracle Grid Control 11g para Solaris/Linux plataforma 64bit.

INGREDIENTES:

  • Java versión 1.6u18  (Descargar del archivo histórico de versiones antiguas de Java)

NOTA: En Solaris es necesario descargar el bundle de JDK 32bit y las librerías de 64bit.
Java SE Development Kit 6u18
 jdk-6u18-solaris-sparcv9.sh
Java SE Development Kit 6u18
 jdk-6u18-solaris-sparcv9.tar.Z

  • Weblogic 10.3.2 (Descargar del archivo histórico de versiones de Weblogic)

  • Oracle11gR2 (11.2.0.2)
  • Oracle Grid Control 11.1.0.1

PASOS A SEGUIR:



1.- Instale la versión de java en un directorio a su gusto, y compruebe la versión.


oracle@leg93uxgrid:~$ /u01/app/oracle/java/jdk1.6.0_18/bin/java -version
java version «1.6.0_18»
Java(TM) SE Runtime Environment (build 1.6.0_18-b07)
Java HotSpot(TM) Server VM (build 16.0-b13, mixed mode)
2.- Tras comprobar con un xclock que las Xwindows están habilitadas, ejecute el weblogic invocando el java recién instalado.


oracle@leg00uxgrid:~$ /u01/app/jdk-6u18/jdk1.6.0_18/bin/java -d64 -jar /software/wls1032_generic.jar
Extracting 0%……………………………………………………………………….100%
2.1.- Escoger “Typical” con las opciones por defecto.
2.2.- En la última pantalla “Installation Complete” DESACTIVAR la opción de “Run Quickstar”.
2.3.- Done.
4.- SOLO EN LINUX. Instalar el parche de Weblogic WDJ7
oracle@leg00uxgrid:~$ /u01/app/Middleware/utils/bsu/bsu.sh
5.- Verifique los parches e instale el software de Oracle11g.
6.- Cree una base de datos para el respositorio del Grid Control.
                6.1.- Escoger el modo “Personalizado” y desmarcar la opción “Configure Enterprise Manager”.
                6.2.- En el apartado de componentes, desmarcar “Enterprise Manager Repository”.
                6.3.- Definir los parámetros de procesos a más de 500 y session_cachad_cursors a 550
                6.4.- Aumentar el datafile de UNDO a 2Gb
7.- Desactivar dbconsole y el repositorio de Enterprise Manager de la base de datos recién creada en caso de que exista (al haber ejecutado incorrectamente el paso 6.1).
oracle@leg93uxgrid:~$ emca -deconfig dbcontrol db -repos drop
STARTED EMCA at 13/06/2011 13:17:30
EM Configuration Assistant, Version 11.2.0.0.2 Production
Copyright (c) 2003, 2005, Oracle.  All rights reserved.
Enter the following information:
Database SID: omsgrid
Listener port number: 1521
Password for SYS user: 
Password for SYSMAN user: 
———————————————————————-
WARNING : While repository is dropped the database will be put in quiesce mode.
———————————————————————-
Do you wish to continue? [yes(Y)/no(N)]: y


8.- Habilitar el usuario DBSNMP.


SQL> alter user DBSNMP account unlock;
User altered.
SQL> alter user DBSNMP identified by grid13c1sa;
User altered.
9.-  Instalar Oracle Enterprise Manager Grid Control 11g.