viernes, 6 de diciembre de 2019

7. ALGO DE AFINAMIENTO Y OPTIMIZACIÓN ORACLE

7.1 EL REPORTE AWR

·         Generar el reporte AWR

- El AWR realiza la recolección de datos estadísticos y de rendimiento de una base de datos Oracle que incluye:
-  Eventos Wait usados para identificar problemas de rendimiento.
-  Estadísticas de Sistema y sesiones.
-  Estadísticas de uso de objetos.
-  Estadísticas de sentencias SQL, Select, Insert, etc.
-  Por defecto Oracle AWR realiza la recolección de datos cada hora con una retención de 7 días

 Desde sqlplus se ejecuta el siguiente script:

SQL>@$ORACLE_HOME/rdbms/admin/awrrpt.sql (en Linux, AIX, Solaris)

En la ruta donde está ubicado se genera un archivo.

Si la base de datos tiene varios incarnation

SQL>@$ORACLE_HOME/rdbms/admin/awrrpti.sql

Nota: En caso de que los pack DIAGNOSTIC Y TUNING de la base de datos se encuentren deshabilitados, no se capturaran datos para este informe.

7.2 SOBRE LOS CURSORES

Una de las mayores dificultades para la base de datos es el volumen de cursores abiertos en la ejecución de sentencias, esto puede radicar en una mala práctica del código de la aplicación que ejecuta las sentencias.
Estas son algunas consultas sobre los cursores.

select user_name, status, osuser, machine, C.SQL_FULLTEXT
from v$session b, v$open_cursor a, V$SQLAREA c
WHERE A.SID = b.SID
and C.ADDRESS = a.ADDRESS
and status='INACTIVE' ;

select a.value, s.username, s.sid, s.serial#
from v$sesstat a, v$statname b, v$session s
where a.statistic# = b.statistic#  and s.sid=a.sid
and b.name = 'opened cursors current';

select sum(a.value),username
from v$sesstat a, v$statname b, v$session s
where a.statistic# = b.statistic#  and s.sid=a.sid
and b.name = 'opened cursors current'
group by username;

select max(a.value) as highest_open_cur, p.value as max_open_cur
from v$sesstat a, v$statname b, v$parameter p
where a.statistic# = b.statistic#
and b.name = 'opened cursors current'
and P.name= 'open_cursors'
group by p.value;

SELECT s.osuser, s.username, s.sid, count(*)
     FROM v$session s, v$open_cursor oc
     WHERE s.saddr = oc.saddr AND
           S.USERNAME is not null
     group by S.OSUSER, S.USERNAME, S.sid;
    
     SELECT *
FROM V$PARAMETER
where name like '%curso%';

7.3 OBJETOS INVALIDOS

·        La siguiente sentencia permite hacer un conteo de todos los objetos en estado inválido en la base de datos sin importar el esquema propietario.

SQL> select COUNT(*) from dba_objects where status='INVALID';

La siguiente sentencia suministra la información del Esquema, nombre y tipo de objeto que se encuentra en estado Inválido.
                  SQL> select owner,object_name,object_type,status from dba_objects where status='INVALID';
·        Es posible con un scripts que trae la base de datos realizar un proceso de recompilación automática, únicamente los objetos que no tengan errores quedaran en estado valido. Este proceso se ejecuta desde la consola.
sqlplus "/as sysdba"

7.3.1 LAS FUNCIONES Y PROCEDIMIENTOS DES-COMPILADOS

La siguiente sentencia dinámica genera los scripts pertinentes para compilar los objetos de tipo Función o Procedimiento.
SQL>select 'alter '||object_type||' '||owner||'.'||object_name||' compile;'
from dba_objects
where status='INVALID'
and object_type in('FUNCTION','PROCEDURE');


7.3.2 LOS OBJETOS DE TIPO PACKAGE des-compilados

·       La siguiente sentencia dinámica genera los scripts pertinentes para compilar los objetos de tipo Package.

SQL>select 'alter '||object_type||' '||owner||'.'||object_name||' compile;'
from dba_objects
where status='INVALID'
and object_type in('PACKAGE')

La siguiente sentencia dinámica genera los scripts pertinentes para compilar los objetos de tipo Package Body.
SQL>select 'alter package '||owner||'.'||object_name||' compile  body;'
from dba_objects
where STATUS='INVALID'
and object_type in('PACKAGE BODY');


7.3.3 LOS triggers en estado disable

·         La siguiente sentencia extrae los Triggers que se encuentran en estado DISABLE.
SQL>select t.OWNER,t.TRIGGER_NAME,t.TABLE_NAME,t.TRIGGER_TYPE
from dba_triggers t
where status='DISABLED'
and owner not in ('SYS','SYSTEM','SCOTT','AURORA$JIS$UTILITY$','AURORA$ORB$UNAUTHENTICATED','OLAPDBA','OLAPSYS','OLAPSVR',
'ORDPLUGINS','ORDSYS','OSE$HTTP$ADMIN','OUTLN','PUBLIC','QS','QS_ADM','QS_CB','QS_CBADM','QS_CS','QS_ES','QS_OS','QS_WS',
'RMAN','SH','WKSYS','CTXSYS','MDSYS','OE','WMSYS','DBSNMP','ANONYMOUS','XDB','TRACESVR','DIP','DMSYS','EXFSYS','MDDATA',
'MGMT_VIEW','ORACLE_OCM','SI_INFORMTN_SCHEMA','SYSMAN','TSMSYS');

Para habilitar los trigger se puede usar la siguiente sentencia dinámica la cual activa los triggers.
SQL> select 'ALTER TRIGGER ' || OWNER || '.' || t.TRIGGER_NAME || ' ENABLE;'
from dba_triggers t
where status='DISABLED'
and owner in ('USUARIO');

7.3.4 LOS TRIGGERS EN ESTADO INVÁLIDO

·        Existe una forma para compilar los trigger de forma manual, para ello se debe tener el propietario del trigger y el nombre del mismo.
             SQL>select owner,object_name,object_type,status FROM DBA_OBJECTS WHERE STATUS='INVALID' AND                       OBJECT_TYPE ='TRIGGER';
y luego de forma manual se ejecuta la siguiente sentencia.

SQL>EXEC DBMS_DDL.alter_compile (' TIPE_OBJETO ', ' SQUEMA ', ‘NOMBRE TRIGGER’);

O

SQL> alter trigger “OWNER.TRIGGER" compile;


·         La siguiente sentencia dinámica genera los scripts pertinentes para compilar los objetos de tipo TRIGGER que se encuentran en estado INVALIDO

SQL> select 'EXEC DBMS_DDL.alter_compile ('||OBJECT_TYPE||','||OWNER||','||OBJECT_NAME||');'FROM DBA_OBJECTS WHERE STATUS='INVALID' AND OBJECT_TYPE ='TRIGGER';

7.3.5 LOS SINONIMOS INVALIDOS

·        La siguiente sentencia extrae los scripts de los SINONIMOS que se encuentran en estado INVALIDO.
 SQL> select 'alter public synonym '||object_name||' compile;' from all_objects where status='INVALID' and object_type='SYNONYM';
Al persistir el estado de INVALIDO del sinónimo es necesario recrearlo.

SQL> SELECT 'CREATE OR REPLACE SYNONYM "'||OB.OWNER||'"."'||OB.OBJECT_NAME||'" FOR "'||DB.TABLE_OWNER||'"."'||DB.TABLE_NAME||'";'FROM DBA_OBJECTS OB, DBA_SYNONYMS DB WHERE OB.OWNER = DB.OWNER AND OB.OBJECT_NAME = DB.SYNONYM_NAME AND OB.STATUS='INVALID' AND OB.OBJECT_TYPE ='SYNONYM';


7.3.6 LOS CONSTRAINT EN ESTADO INACTIVO


La siguiente sentencia permite validar los constraint que se encuentran en estado inactivo. Suministra la información del Esquema, nombre del constraint, tipo, tabla, estado y fecha de modificación.

SQL>SELECT owner,constraint_name, decode(constraint_type, 
'C', 'Check', 'O', 'R/O View', 'P', 'Primary',        'R', 'Foreign', 'U', 'Unique', 'V', 'Check view') type, table_name, status, last_change FROM DBA_CONSTRAINTS WHERE STATUS='DISABLED' and owner not in ('SYS','SYSTEM','SCOTT','AURORA$JIS$UTILITY$','AURORA$ORB$UNAUTHENTICATED','OLAPDBA','OLAPSYS','OLAPSVR', 'ORDPLUGINS','ORDSYS','OSE$HTTP$ADMIN','OUTLN','PUBLIC','QS','QS_ADM','QS_CB','QS_CBADM','QS_CS','QS_ES','QS_OS','QS_WS', 'RMAN','SH','WKSYS','CTXSYS','MDSYS','OE','WMSYS','DBSNMP','ANONYMOUS','XDB','TRACESVR','DIP','DMSYS','EXFSYS','MDDATA', 'MGMT_VIEW','ORACLE_OCM','SI_INFORMTN_SCHEMA','SYSMAN','TSMSYS');
La siguiente sentencia dinámica genera los scripts para modificar el estado de los constraint que presentan estado DISABLE cambiándolo a ENABLE. 
SELECT 'alter table ' || owner || '.' || table_name || ' enable constraint ' || constraint_name || ';' FROM DBA_CONSTRAINTS WHERE STATUS='DISABLED' and owner not in ('SYS','SYSTEM','SCOTT','AURORA$JIS$UTILITY$','AURORA$ORB$UNAUTHENTICATED','OLAPDBA','OLAPSYS','OLAPSVR', 'ORDPLUGINS','ORDSYS','OSE$HTTP$ADMIN','OUTLN','PUBLIC','QS','QS_ADM','QS_CB','QS_CBADM','QS_CS','QS_ES','QS_OS','QS_WS', 'RMAN','SH','WKSYS','CTXSYS','MDSYS','OE','WMSYS','DBSNMP','ANONYMOUS','XDB','TRACESVR','DIP','DMSYS','EXFSYS','MDDATA', 'MGMT_VIEW','ORACLE_OCM','SI_INFORMTN_SCHEMA','SYSMAN','TSMSYS');
7.3.7 VALIDACIÓN DE INDICES

-        La consulta extrae todos los índices de la base de datos que se encuentran en estado UNUSABLE

SQL> select * from dba_indexes 
where status='UNUSABLE'; 
-          La consulta extrae todos los índices particionados de la base de datos que se encuentran en estado UNUSABLE

SQL>select * from dba_ind_PARTITIONS where status='UNUSABLE' 
-        Sentencia dinámica que genera los scripts para la re-creación de los índices en estado Unusable.

SQL> select 'ALTER INDEX ' || OWNER || '.' || INDEX_NAME || ' REBUILD;'  from dba_indexes where STATUS='UNUSABLE' union select 'ALTER INDEX ' || INDEX_OWNER || '.' || INDEX_NAME || ' REBUILD PARTITION ' || PARTITION_NAME || ';'  from dba_ind_partitions where STATUS='UNUSABLE' union select 'ALTER INDEX ' || INDEX_OWNER || '.' || INDEX_NAME || ' REBUILD SUBPARTITION ' || PARTITION_NAME || ';' from dba_ind_subpartitions where STATUS='UNUSABLE'; 
-          MOVER INDICES A DETERMINADO TABLESPACE

El script dinámico genera la sintaxis de las sentencias que se deben ejecutar para mover los indices a otro tablespace.
Se debe incluir el nombre del tablespace destino de los índices, el Esquema dueño de los indices a mover y el nombre del tablespace en el cual se encuentran los índices que serán movidos.

SQL>select 'alter index ' || owner || '.' || index_name || ' rebuild tablespace PROVEEDORES_INDX_32K;' from dba_indexes where owner in ('PROVEEDORES_13') and tablespace_name='PROVEEDORES_13'; 
SQL>SELECT 'alter index ' || owner || '.' || segment_name || ' rebuild tablespace GIPS_IDX;' FROM DBA_SEGMENTS WHERE OWNER='GIPS' AND SEGMENT_TYPE='INDEX' AND TABLESPACE_NAME='USERS';
7.3.8 COMPILAR LOS OBJETOS DE UN ESQUEMA

·         La siguiente sentencia  permite compilar todos los objetos inválidos de un ESQUEMA específico, se debe digitar el nombre del esquema.

SQL> exec dbms_utility.compile_schema('NOMBRE_ESQUEMA'); (ejecutar con sys)

Cabe anotar que este procedimiento toma bastante tiempo su ejecución y es necesario tener bloqueados todos los accesos a la base de datos.

7.3.9 COMPILAR Las vistas materializadas INVÁLIDAS

·        La siguiente sentencia dinámica genera los scripts para la re-compilación de las vistas materializadas en estado INVALID.

SQL>select 'alter materialized view '||owner||'.'||object_name|| ' compile;' from all_objects where status='INVALID' and object_type='MATERIALIZED VIEW';

7.3.10 COMPILAR Las vistas invalidas

·        La siguiente sentencia dinámica genera los scripts para la re-compilación de las vistas en estado INVALID.

SQL>select 'alter view '||owner||'.'||object_name|| ' compile;' from all_objects where status='INVALID' and object_type='VIEW';

7.3.11 COMPILAR OBJETOS DE JAVA CLASS  INVÁLIDOS

·        La siguiente sentencia dinámica genera los scripts para la re-compilación de los objetos  JAVA CLASS cuyo estado es INVALID.

SQL>Select 'alter java class "'||object_name||'" compile;' from user_objects where status <> 'VALID' And object_type ='JAVA CLASS';

7.4 SOBRE ESTADÍSTICAS

·         Estadísticas de acceso a datos.

-          Con los siguientes comandos se pueden realizar la captura de estadísticas para algunos objetos de la base de datos.

A)    Captura de estadísticas en tablas, ejemplos

SQL>exec dbms_stats.gather_table_stats(ownname => 'USUARIO', tabname => 'NOMBRE_TABLA');

B)    Captura de estadísticas en índices, ejemplos

SQL>exec dbms_stats.gather_index_stats(ownname => 'USUARIO', indname => 'NOMBRE_INDICE');

C)    Es posible actualizar las estadísticas de un esquema específico completo.

SQL>exec DBMS_STATS.gather_schema_stats('USUARIO');

D)    Actualizar estadísticas de la base de datos completa

SQL>exec DBMS_STATS.GATHER_DATABASE_STATS;

-          Si después de tomar las estadísticas se presentan problemas es posible eliminar las estadísticas tomadas

i)                   Eliminación de estadísticas en tablas, ejemplos.

SQL>EXEC dbms_stats.delete_table_stats(ownname => 'USUARIO', tabname => 'NOMBRE_TABLA');

ii)                 Eliminación de estadísticas en índices, ejemplos.

SQL>EXEC dbms_stats.delete_index_stats(ownname => 'USUARIO', indname => 'NOMBRE_INDICE');

iii)               Eliminación de estadísticas de un esquema completo.

SQL>exec DBMS_STATS.delete_schema_stats('USUARIO');

7.5 ALGO SOBRE LOS LOBS

El LOB en Oracle, es el encargado de gestionar los tipos de datos no estructurados o estructurados como texto, gráficos, audio y video y una de sus características principales es el gran tamaño que dichos campos pueden manejar como largas estructuras de información(puede alcanzar hasta 4GB). Pero ante lo anterior también el crecimiento sobre la base de datos es fuerte, por lo tanto es necesaria la revisión para su optimización:

-          Las siguientes sentencias suministran información de los segmentos LOBS que se encuentran en la base de datos
La siguiente sentencia extracta información del Esquema, tabla, columna y nombre del segmento LOB, adicional el Tablespaces que lo almacena y el nombre del índice. Los segmentos LOBS listados no pertenecen a Esquemas del sistema.

SQL> SELECT *
FROM DBA_LOBS
where owner NOT in ('SYS','SYSTEM','SCOTT','AURORA$JIS$UTILITY$','AURORA$ORB$UNAUTHENTICATED','OLAPDBA','OLAPSYS','OLAPSVR','ORDPLUGINS','ORDSYS','OSE$HTTP$ADMIN','OUTLN','PUBLIC','QS','QS_ADM','QS_CB','QS_CBADM','QS_CS','QS_ES','QS_OS','QS_WS','RMAN','SH','WKSYS','CTXSYS','MDSYS','OE','WMSYS','DBSNMP','ANONYMOUS','XDB','TRACESVR','DIP','PERFSTAT','DMSYS','EXFSYS','MDDATA','MGMT_VIEW','ORACLE_OCM','SI_INFORMTN_SCHEMA','SYSMAN','TSMSYS');

-          La siguiente sentencia extracta información del Esquema, nombre del segmento LOB, tamaño actual del segmento y Tablespaces en el cual se almacena el LOB. Los segmentos LOBS listados no pertenecen a Esquemas del sistema

SELECT owner, segment_name, bytes/1024/1024, tablespace_name
FROM DBA_SEGMENTS
where segment_name in (
SELECT segment_name
FROM DBA_LOBS
where owner not in ('SYS','SYSTEM','SCOTT','AURORA$JIS$UTILITY$','AURORA$ORB$UNAUTHENTICATED','OLAPDBA','OLAPSYS','OLAPSVR','ORDPLUGINS','ORDSYS','OSE$HTTP$ADMIN','OUTLN','PUBLIC','QS','QS_ADM','QS_CB','QS_CBADM','QS_CS','QS_ES','QS_OS','QS_WS','RMAN','SH','WKSYS','CTXSYS','MDSYS','OE','WMSYS','DBSNMP','ANONYMOUS','XDB','TRACESVR','DIP','PERFSTAT','DMSYS','EXFSYS','MDDATA','MGMT_VIEW','ORACLE_OCM','SI_INFORMTN_SCHEMA','SYSMAN','TSMSYS'));

-          La siguiente sentencia suministra el tamaño en Megabytes de los segmentos LOBS que contiene la base de datos y que no pertenecen a Esquemas del sistema.

SQL> SELECT sum(BYTES/1024/1024) MEGABYTES
FROM DBA_SEGMENTS
where segment_name in (
SELECT segment_name
FROM DBA_LOBS
where owner not in ('SYS','SYSTEM','SCOTT','AURORA$JIS$UTILITY$','AURORA$ORB$UNAUTHENTICATED','OLAPDBA','OLAPSYS','OLAPSVR','ORDPLUGINS','ORDSYS','OSE$HTTP$ADMIN','OUTLN','PUBLIC','QS','QS_ADM','QS_CB','QS_CBADM','QS_CS','QS_ES','QS_OS','QS_WS','RMAN','SH','WKSYS','CTXSYS','MDSYS','OE','WMSYS','DBSNMP','ANONYMOUS','XDB','TRACESVR','DIP','PERFSTAT','DMSYS','EXFSYS','MDDATA','MGMT_VIEW','ORACLE_OCM','SI_INFORMTN_SCHEMA','SYSMAN','TSMSYS'));

-          La siguiente sentencia permite mover un segmento Lob a otro tablespace. se debe incluir el nombre de la tabla (<your_table>), la columna Lob (<lob_column>) y el tablespace hacia el cual será movido el segmento.

SQL> alter table <your_table> move lob (<lob_column>) store as (tablespace <tablespace_lob>);

SQL> select 'alter table '||owner||'.'|| table_name || ' move lob ('|| column_name ||') store as (tablespace PNET_LOBS_32K);'
from dba_lobs
where owner in ('PNET');

7.6 SOBRE LA MEMORIA.

-          Uno de los objetivos para la optimización de la base de datos, es el de proporcionar información de las sentencias de mayor consumo en la memoria asignada a la base de datos propiamente la SGA y PGA, el siguiente query permite gestionar las sentencias en memoria con mayor consumo y su cantidad de ejecuciones.

SQL>SELECT sql_id, executions, FIRST_LOAD_TIME,SHARABLE_MEM,DISK_READS,BUFFER_GETS,ROWS_PROCESSED
FROM V$SQL
ORDER BY EXECUTIONS DESC;

Para consultar la sentencia especifica:

SELECT * FROM V$SQLTEXT WHERE SQL_ID='<sql_id>';

SELECT * FROM V$SQLAREA WHERE SQL_ID='<sql_id>yfhydmk9';


7.6.1 INDICADOR BUFFER HIT RATIO

·       Uno de los seguimientos más recomendados sobre la ejecución de las sentencias SQL en la base de datos, es la lectura sobre el parámetro HIT RATE, Es un valor porcentual que va desde 0 hasta 100. Este valor representa una relación de lecturas de bloques consistentes (Lecturas/Escrituras en la SGA, para ser más exacto en la Buffer Cache) vs las lecturas físicas (Lecturas/Escrituras de bloques de datos en disco) que se realizan al ejecutar un SQL. Lo óptimo es que este valor este entre 85% y 100%, ya que eso nos indicaría que la sentencia trabaja más sobre los bloques de datos de la Buffer Cache que sobre los bloques de datos de los datafile almacenados en disco, si el valor retornado es menor a 85 es necesario analizar la sentencia para ver que se puede optimizar.

-          SQL con Hit Rate inferior al 85% y con más de 1000 bloques leídos en disco, ordenados de manera ascendente por el Hit Rate

SQL>SELECT sql_text, disk_reads, buffer_gets, module,
(buffer_gets - disk_reads) / buffer_gets hit_rate
FROM v$sqlarea
WHERE buffer_gets != 0
AND (buffer_gets - disk_reads) / buffer_gets < .85
AND disk_reads > 1000
ORDER BY 4;

-          SQL con más de 1000 bloques leídos en disco, ordenados de manera descendente por el campo Disk_Reads

SQL>SELECT sql_text, disk_reads, buffer_gets, module,
(buffer_gets - disk_reads) / buffer_gets hit_rate
FROM v$sqlarea
WHERE buffer_gets != 0
AND disk_reads > 1000
ORDER BY 4;

-        La siguiente sentencia permite realizar un seguimiento sobre el indicador BUFFER HIT RATIO, el cual debería estar con un valor superior al 96%, lo que permite validar la eficiencia de los bloques Buffer utilizados por la base de datos.

select
   1 - ((a.value - (b.value))/d.value) "Cache Hit Ratio"
from
   v$sysstat a,
   v$sysstat b,
   v$sysstat d
where
   a.name='physical reads'
and
   b.name='physical reads direct'
and
   d.name='session logical reads';

-        Para poder adecuar nuevamente el uso de la memoria se debe realizar el siguiente procedimiento:
Existe el parámetro DB_CACHE_SIZE, si el indicador es bajo, se debe incrementar la memoria asignada a este parámetro que por defecto se encuentra en 64MB, se debe empezar a probar con valores un poco más altos como 100MB.

-        La siguiente sentencia permite hacer la misma verificación del indicador del BUFFER HIT RATIO pero de forma individual por cada usuario logeado en la base de datos.

SQL>SELECT a.username "Username",
       b.consistent_gets "Consistent Gets",
       b.block_gets "DB Block Gets",
       b.physical_reads "Physical Reads",
       Round(100* (b.consistent_gets + b.block_gets - b.physical_reads) /
       (b.consistent_gets + b.block_gets),2) "Hit Ratio %"
FROM   v$session a,
       v$sess_io b
WHERE  a.sid = b.sid
AND    (b.consistent_gets + b.block_gets) > 0
AND    a.username IS NOT NULL;

7.6.2 ACERCA DEL SHARED POOL

-          La siguiente sentencia consolida la información de la SGA con el tamaño de memoria que tiene.

SQL> select pool, sum(bytes)/1024/1024  from v$sgastat group by pool;

-        La siguiente sentencia extracta el tamaño de cada componente que se encuentra en el área de memoria  SHARED_POOL.

SQL> SELECT *
  FROM v$sgastat
 WHERE pool = 'shared pool'
   AND (NAME IN
           ('free memory', 'sql area', 'library cache', 'miscellaneous',
            'row cache', 'KGH: NO ACCESS')
       );

-        La siguiente sentencia libera el área de memoria SHARED_POOL (VALIDAR BIEN EL MOMENTO EN QUE SE VA A EJECUTAR)

SQL> ALTER SYSTEM FLUSH SHARED_POOL;

-        Permite validar el indicador de eficiencia de la shared pool y library cache, Si el porcentaje es inferior al 99% es un indicador de contención.

SQL> SELECT
name, (1-(misses/gets))*100
AS "Ratio", sleeps
FROM v$latch
WHERE name in ('library cache', 'shared pool');

-        La siguiente sentencia permite validar la relación de eficiencia de los RedoLog.

SQL> SELECT h.pid, n.name, (l.misses/l.gets)*100 wait_ratio
FROM v$latchholder h, v$latchname n, v$latch l
WHERE h.laddr = l.addr
AND l.latch# = n.latch#
AND n.name in ('redo allocation', 'redo copy');

Tenga en cuenta que si la relación  un output y el wait_ratio  es superior a 1, hay un problema. A continuación, tendrá que aumentar el tamaño del Redo Log Buffer.

-        La siguiente sentencia suministra los objetos con mayor número de ejecuciones

SQL> SET PAGESIZE 90
Set linesize 500
column owner format a13
column name_type format a10
column name format a70
select  owner, name||' - '||type name, executions from v$db_object_cache
where executions  > 100
  and type in ('PACKAGE', 'PACKAGE BODY', 'FUNCTION', 'PROCEDURE')
order by executions  desc;

·       Sentencia que permite ver como sería el comportamiento de la base de datos de acuerdo a un valor de memoria asignado

SELECT *
FROM V$SGA_TARGET_ADVICE
ORDER BY 1;

SELECT *
FROM V$PGA_TARGET_ADVICE
ORDER BY 1;

7.7 ACERCA DEL RECYCLEBIN

-        Existe en Oracle una especie de papelera de reciclaje sobre los objetos de los esquemas que han sido eliminados:
Las siguientes sentencias muestran información de los objetos que se encuentran en la papelera de reciclaje de la base de datos

SQL> select count(1) from dba_recyclebin;

-        La siguiente sentencia suministra la información en Megabytes del espacio a liberar de la papelera de reciclaje.

SQL>SELECT sum(bytes/1024/1024) LIBERADO
FROM DBA_RECYCLEBIN R, DBA_SEGMENTS S
where segment_name=R.object_name;

-        La siguiente sentencia permite liberar los objeto que se encuentran en la papelera de reciclaje

SQL> purge dba_recyclebin;

-        El parámetro es dinámico en 10g y no necesita reinicio de la base de datos. Para 11g es necesario reiniciar.

SQL> alter system set recyclebin=off scope=both;

7.8 SENTENCIAS DE ALTO CONSUMO Y SESSIONES PEGADAS

7.8.1 identificar usuarios activos y con ejecuciones en la base de datos

·        Existen diferentes procesos sobre la base de datos que nos generan alto consumo, tanto de memoria como de espacio, para lo anterior se debe tener en cuenta el análisis tanto de sistema operativo como del mismo motor de la base de datos.

-          Con la siguiente sentencia se puede determinar los usuarios conectados actualmente a la base de datos.

SELECT V$SESSION.LOGON_TIME CONEXION,
SUBSTR(V$PROCESS.SPID,1,5) UNIX,
SUBSTR(TO_CHAR(V$SESSION.SID),1,3) SID,
SUBSTR(V$SESSION.USERNAME,1,10) USUARIO,
SUBSTR(TO_CHAR(V$SESSION.SERIAL#),1,6) NSERIE,
SUBSTR(V$SESSION.OSUSER,1,10) OSUSER,
SUBSTR(V$SESSION.STATUS,1,10) ESTADO,
SUBSTR(V$SESSION.SERVER,1,7) SERVER,
SUBSTR(V$SESSION.MACHINE,1,30) MAQUINA,
SUBSTR(V$SESSION.SECONDS_IN_WAIT,1,10) SEGUNDOS_EN_ESPERA
FROM
V$SESSION,
V$PROCESS
WHERE
(V$SESSION.PADDR = V$PROCESS.ADDR) AND
(V$SESSION.USERNAME IS NOT NULL) AND
(V$SESSION.SERVER LIKE 'DEDIC%') AND
SUBSTR(V$SESSION.STATUS,1,10) LIKE 'ACTIV%'
ORDER BY V$PROCESS.SPID;

7.8.2 BUSCAR LA SESSION POR PID Y VER LA QUERY SQL

·         En Oracle tenemos un montón de tablas y vistas que nos permiten obtener muchos datos de la instancia. Vamos a ver algunos campos de las vistas v$session, v$process y v$sqlarea para obtener el identificador de sesión y la query para poder matar las query a partir del PID del sistema operativo.
En la vista v$session tenemos información de las sesiones, los campos que para este caso nos interesan son:
-          SID (identificador de sesión) combinado con el SERIAL# (serial de sesión) nos sirven para matar la query.
-          PADDR: Dirección del proceso de la sesión, debemos combinarlo con v$process para obtener más datos
-          SQL_ID: Identificador de la sentencia que se ejecuta, debemos combinarlo con v$sqlarea para obtener más datos.

En la vista v$process podremos obtener más datos sobre el proceso, entre ellos el PID del sistema operativo:
-          ADDR: Dirección del proceso de la sesión, el cual lo tenemos de v$session.
-          PID: Identificación de proceso de Oracle, se trata de un identificador interno
-          SPID: Identificación de proceso del sistema operativo
Por lo tanto, combinando las dos vistas podemos obtener los SID y SERIAL# para todas las sesiones con su correspondiente PID de sistema operativo:

SQL> select SID, s.SERIAL#, SPID from v$session s, v$process p
     where s.paddr=p.addr;

En el caso que quisiéramos obtener el SID y el SERIAL# para un PID concreto solo debemos añadir una condición al WHERE:

SQL> select SID, s.SERIAL#, SPID from v$session s, v$process p
     where s.paddr=p.addr and SPID='28285';

·       Para obtener la query que está ejecutando una sesión deberemos ver la vista v$sqlarea, algunos de sus campos son:
SQL_ID: Identificador de la sentencia que se ejecuta.
SQL_TEXT: Primeros 1000 caracteres de la sentencia.
SQL_FULLTEXT: La sentencia entera.

Por lo tanto, para ver la sentencia de todas las sesiones ejecutaríamos la siguiente query:
SQL> select SID, s.SERIAL#, SPID, a.sql_text from v$session s, v$process p, v$sqlarea a where s.paddr=p.addr and s.sql_id=a.sql_id; 

7.8.3 SESSIONES DE ALTO CONSUMO DE CPU

·         La siguiente sentencia nos permite determinar las sesiones que tienen alto consumo de CPU

SQL> column s.username format a10
column s.serial# format a10
column s.p.spid format a10
column s.module format a10
column a.SQL_ID format a10
SELECT s.sid, s.serial#, p.spid as "OS PID",s.username, s.module, st.value/100 as "CPU sec", a.SQL_ID
FROM v$sesstat st, v$statname sn, v$session s, v$process p, v$sqlarea a
WHERE sn.name = 'CPU used by this session' -- CPU
AND st.statistic# = sn.statistic#
AND st.sid = s.sid
AND s.paddr = p.addr
AND s.last_call_et < 1800 -- active within last 1/2 hour
AND s.logon_time > (SYSDATE - 240/1440) -- sessions logged on within 4 hours
AND s.sql_id=a.sql_id
ORDER BY st.value;

·         La siguiente sentencia le permite identificar que script se está ejecutando,
De la consulta anterior se toma el valor de SQL_ID y se reemplaza en la variable con ese nombre.

SQL>column SQL_ID format a15
column SQL_TEXT format a100
select SQL_ID, SQL_TEXT from v$sqlarea where SQL_ID='9g81c49zdvn2b';


7.8.4 MATAR SESSIONES

·         Identificado el SID y el SERIAL# de la sesión se puede proceder a matar su conexión.

SQL> ALTER SYSTEM KILL SESSION 'sid,serial#' immediate;

·         Oracle 11g introduce la sintaxis ALTER SYSTEM DISCONNECT SESSION  un Nuevo método de matar una sesion Oracle.  Lo que hace este comando es matar el proceso de servidor dedicado ( o circuito virtual cuando se utilizan los Shared Sever), lo que es equivalente a matar el proceso desde el sistema operativo. La sintaxis básica es similar a la del comando KILL SESSION más la cláusula POST_TRANSACTION.

SQL> ALTER SYSTEM DISCONNECT SESSION 'sid,serial#' POST_TRANSACTION;
SQL> ALTER SYSTEM DISCONNECT SESSION 'sid,serial#' IMMEDIATE;

·         La siguiente sentencia suministra la información de las sesiones que está ejecutando un Esquema específico y la presenta ordenándolas por el campo PGA_MAX_MEM (Máxima cantidad de memoria asignada al proceso).

SQL> column username format a12
column status format a10
column machine format a10
column sql_id format a15
column state format a10
select s.sid,s.serial#,s.username,status,machine, sql_id, logon_time, state, pga_used_mem, pga_alloc_mem, pga_freeable_mem,pga_max_mem
from v$session s,v$process p
where p.ADDR=s.PADDR
and sChemaname='MFE_USER'
order by p.PGA_MAX_MEM;

-          La siguiente sentencia suministra el script completo que se encuentra identificado con el sql_id suministrado.

SQL>select s.SQL_FULLTEXT
from v$sql s
where s.SQL_ID ='d4bawfjxjypnq';

7.9 ACERCA DE OBJETOS

7.9.1 mover tablas a otro tablespace

·         La siguiente sentencia permite mover una tabla a otro tablespace

SQL>SELECT 'alter table ' || OWNER || '.' || table_NAME || ' move tablespace GIPS;'--  , num_rows
from dba_tables
where owner in ('GIPS')
AND TABLESPACE_NAME='USERS';

·       La siguiente sentencia permite mover una tabla con un referente Lob a otro tablespace.
Se debe incluir el nombre de la columna  (<COLUMN_NAME>) y el tablespace hacia el cual será movido el segmento.

SELECT 'ALTER TABLE ' || owner || '.' || table_name ||  ' MOVE LOB (' || COLUMN_NAME || ') STORE AS (TABLESPACE GIPS); '
FROM DBA_LOBS
WHERE OWNER IN ('GIPS')
AND TABLESPACE_NAME='USERS';

7.9.2 DETALLE DE TABLAS DE UN USUARIO REGISTROS Y TAMAÑOS

·       Con el objetivo de verificar el tamaño de una tabla específica de un esquema, su cantidad de registros entre otros datos y con el fin de depurar o afinar la DB se recomienda ejecutar la siguiente sentencia.

SQL>SELECT t.table_name,
       t.tablespace_name,
       t.num_rows,
       t.avg_row_len,
       t.blocks,
       t.empty_blocks,
       ROUND(t.blocks * ts.block_size/1024/1024,2) AS size_mb
FROM   dba_tables t
       JOIN dba_tablespaces ts ON t.tablespace_name = ts.tablespace_name
WHERE  t.owner = UPPER('UXXIAC')


ORDER BY t.table_name;

7.10 BORRADO MASIVO DE OBJETOS

En ocasiones es necesario depurar los objetos inválidos o borrarlos para afinar la base de datos, para hacer este proceso es necesario estar seguro de que dichos objetos no son necesarios. Se recomienda generar un backup de la base de datos para crear un punto de restauración de ser necesario (ver: 9. SOBRE BACKUP Y RESTAURACION DE BASES DE DATOS).  Y de igual manera generar los siguientes scripts con el usuario SYS o SYSTEM.

7.10.1 BORRAR TRIGGERS INVALIDOS EN FORMA MASIVA

· La siguiente sentencia dinámica genera los scripts para el borrado masivo de todos los TRIGGERS en estado INVÁLIDO.

SQL>select 'drop trigger "'||owner||'"."'||object_name||'";'FROM DBA_OBJECTS WHERE STATUS='INVALID' AND OBJECT_TYPE ='TRIGGER';

7.10.2 BORRAR VISTAS (VIEW) INVALIDAS EN FORMA MASIVA

·        La siguiente sentencia dinámica genera los scripts para el borrado masivo de todos las VISTAS en estado INVÁLIDO.

SQL>select 'drop VIEW "'||owner||'"."'||object_name||'";'FROM DBA_OBJECTS
WHERE STATUS='INVALID' AND OBJECT_TYPE ='VIEW';

7.10.3 BORRAR VISTAS materializadas (MATERIALIZED VIEW) INVALIDaS EN FORMA MASIVA

·        La siguiente sentencia dinámica genera los scripts para el borrado masivo de todos las VISTAS MATERIALIZADAS en estado INVÁLIDO.

SQL>select 'drop MATERIALIZED VIEW "'||owner||'"."'||object_name||'";'FROM DBA_OBJECTS
WHERE STATUS='INVALID' AND OBJECT_TYPE ='MATERIALIZED VIEW';

7.10.4 BORRAR procedimientos (procedure) INVALIDOS EN FORMA MASIVA

·        La siguiente sentencia dinámica genera los scripts para el borrado masivo de todos los PROCEDIMIENTOS en estado INVÁLIDO.

SQL>select 'drop procedure "'||owner||'"."'||object_name||'";'FROM DBA_OBJECTS WHERE STATUS='INVALID' AND OBJECT_TYPE ='PROCEDURE';

7.10.5 BORRAR FUNCIONES (FUNCTION) INVALIDOS EN FORMA MASIVA

·         La siguiente sentencia dinámica genera los scripts para el borrado masivo de todas las FUNCIONES en estado INVÁLIDO.

SQL> select 'drop FUNCTION "'||owner||'"."'||object_name||'";'FROM DBA_OBJECTS
WHERE STATUS='INVALID' AND OBJECT_TYPE ='FUNCTION';

7.10.6 BORRAR PAQUETES (PACKAGE) INVÁLIDOS EN FORMA MASIVA

·        La siguiente sentencia dinámica genera los scripts para el borrado masivo de todas los PAQUETES en estado INVÁLIDO.

SQL>select 'drop PACKAGE "'||owner||'"."'||object_name||'";'FROM DBA_OBJECTS
WHERE STATUS='INVALID' AND OBJECT_TYPE ='PACKAGE';

SQL>select 'drop PACKAGE "'||owner||'"."'||object_name||'";'FROM DBA_OBJECTS
WHERE STATUS='INVALID' AND OBJECT_TYPE ='PACKAGE BODY';

7.10.7 BORRAR TODOS LOS OBJETOS DE UN ESQUEMA

·      En ocasiones es necesario borrar todos los objetos de un esquema o varios esquemas de la base de datos pero que no se afecte el usuario que ya se encuentra creado incluyendo que su contraseña queda igual.

La siguiente sentencia dinámica genera los scripts para el borrado masivo de todos los objetos propios del usuario relacionado. (Ejecutar con usuario SYS)

SET ECHO OFF
SET HEADING OFF
SET PAGESIZE 0
SET FEEDBACK OFF
SET TIMING OFF
SET TIME OFF
SET TRIMSPOOL ON
SET TRIMOUT ON
SET LINESIZE 100

SET TERMOUT OFF
SPOOL eliminar-todo

SELECT 'DROP '|| UO.OBJECT_TYPE ||' "'||UO.OWNER||'"."' || UO.OBJECT_NAME || '"' ||
    DECODE(UO.OBJECT_TYPE,
        'CLUSTER', ' INCLUDING TABLES CASCADE CONSTRAINTS',
        'OPERATOR', ' FORCE',
        'TABLE', ' CASCADE CONSTRAINTS',
        'TYPE',  ' FORCE',
        'VIEW', ' CASCADE CONSTRAINTS',
    '') || ';'
FROM All_OBJECTS UO
LEFT JOIN USER_RECYCLEBIN UR ON UO.OBJECT_NAME=UR.OBJECT_NAME
WHERE UO.OBJECT_TYPE IN (
    'CLUSTER', 'CONTEXT', 'DATABASE LINK', 'DIMENSION', 'DIRECTORY', 'FUNCTION', 'INDEX TYPE',
    'JAVA', 'LIBRARY', 'MATERIALIZED VIEW', 'OPERATOR', 'OUTLINE', 'PACKAGE', 'PROCEDURE',
    'SEQUENCE', 'SYNONYM', 'TABLE', 'TYPE', 'VIEW'
)
AND UR.OBJECT_NAME IS NULL
AND OWNER NOT IN('SYS','SYSTEM','SYSMAN','OUTLN','WMSYS','XS$NULL','ORDSYS','ORDDATA','ORDPLUGINS','MDSYS','WKSYS','OLAPSYS','APPQOSSYS','XDB')
ORDER BY UO.OBJECT_TYPE, UO.OBJECT_NAME;

SPOOL OFF
SET TERMOUT ON

·       Nota: Una recomendación sobre los objeto que ya fueron previamente borrados y se encuentran en el RECYCLEBIN es el de ejecutar el purge.

SQL>PURGE RECYCLEBIN;


7.10.8 REGLAS DE INTEGRIDAD Y LA COLUMNA A LA QUE AFECTAN

·       La siguiente sentencia determina las reglas de la integridad y las respectivas columnas que afectan y determina el objeto al cual pertenece la columna.

SQL>SELECT constraint_name, table_name, column_name FROM sys.all_cons_columns;

7.10.9 DESHABILITAR TODAS LAS FOREIGN KEYS DE TODAS LAS TABLAS PARA BORRADO DE DATA

·         Deshabilitar

BEGIN 
  FOR c IN
  (SELECT c.owner, c.table_name, c.constraint_name   
   FROM user_constraints c, user_tables t   
   WHERE c.table_name = t.table_name   
   AND c.status = 'ENABLED'
   AND c.CONSTRAINT_TYPE = 'R'    
   ORDER BY c.constraint_type DESC)  
LOOP    
  dbms_utility.exec_ddl_statement('alter table ' || c.owner ||
  '.' || c.table_name || 'disable constraint ' || c.constraint_name);   
END LOOP;
END;

·         Volver a habilitar

BEGIN 
  FOR c IN
  (SELECT c.owner, c.table_name, c.constraint_name
   FROM user_constraints c, user_tables t
   WHERE c.table_name = t.table_name
   AND c.status = 'DISABLED'
   AND c.CONSTRAINT_TYPE = 'R'   
   ORDER BY c.constraint_type)
LOOP
  dbms_utility.exec_ddl_statement('alter table ' || c.owner ||
   '.' || c.table_name || ' enable constraint ' || c.constraint_name);  
END LOOP;
END;


7.10.10 Borrar estadísticas históricas en tablespace SYSAUX

·       Siempre que las estadísticas en el diccionario se modifican, las versiones antiguas se guardan automáticamente para una futura restauración. Las estadísticas históricas se purgan automáticamente a intervalos regulares en función del valor de retención. La retención es configurable mediante el procedimiento ALTER_STATS_HISTORY_RETENTION, el valor por defecto de esta configuración corresponde a 31 días, que normalmente no es demasiado grande, pero puede ser un problema en bases de datos pequeñas, ya que el tablespace SYSAUX puede crecer en un tamaño no soportado o bien consumir recursos que se tienen designados a otros tablespaces. 
·       A continuación se detalla el procedimiento para corregir el crecimiento excesivo de las tablas asociadas a la retención de estadísticas.

-          Verificar espacio utilizado por estadísticas históricas.

set linesize 120
set pagesize 100
COLUMN "Item" FORMAT A25
COLUMN "Space Used (GB)" FORMAT 999.99
COLUMN "Schema" FORMAT A25
COLUMN "Move Procedure" FORMAT A40
SELECT  occupant_name "Item",
    space_usage_kbytes/1048576 "Space Used (GB)",
    schema_name "Schema",
    move_procedure "Move Procedure"
FROM v$sysaux_occupants
ORDER BY 1
/

NOTA: La vista dinámica V$SYSAUX_OCCUPANTS indica, entre otras cosas, las aplicaciones que usan SYSAUX, el espacio utilizado, el nombre del esquema propietario de la mismas y el nombre del procedimiento que debe emplearse para desplazar determinado contenido desde SYSAUX hasta otra localización.

-        ¿Cuánto tiempo se mantienen las estadísticas históricas?

SQL>select dbms_stats.get_stats_history_retention from dual;

Por defecto son 31 Dias

-          Establecer la retención de las estadísticas históricas 10 días.

SQL>exec dbms_stats.alter_stats_history_retention(10);

-        Borrar las estadísticas históricas de más de 10 días.
Nota: (la mejor manera de hacerlo por etapas si existe una gran cantidad de datos (SYSDATE-30, sydate-25, etc)

exec DBMS_STATS.PURGE_STATS(SYSDATE-10);

-         Mostrar las estadísticas disponibles que no han sido borradas.

SQL>select dbms_stats.get_stats_history_availability from dual;

-         Muestra el tamaño de las tablas a reconstruir después del borrado de las estadísticas históricas.

col Mb form 9,999,999
col SEGMENT_NAME form a40
col SEGMENT_TYPE form a6
set lines 120
select sum(bytes/1024/1024) Mb, segment_name,segment_type from dba_segments
where  tablespace_name = 'SYSAUX'
and segment_name like 'WRI$_OPTSTAT%'
and segment_type='TABLE'
group by segment_name,segment_type order by 1 asc

-        Muestra el tamaño de los indices a reconstruir después del borrado de las estadísticas históricas.

col Mb form 9,999,999
col SEGMENT_NAME form a40
col SEGMENT_TYPE form a6
set lines 120
select sum(bytes/1024/1024) Mb, segment_name,segment_type from dba_segments
where  tablespace_name = 'SYSAUX'
and segment_name like '%OPT%'
and segment_type='INDEX'
group by segment_name,segment_type order by 1 asc
/

7.10.11 Borrar los sinónimos públicos

·       Para poder borrar los sinónimos públicos que estén asociados a objetos o tablas de un esquema es necesario determinar el nombre del sinónimo, de igual manera es necesario invalidar algunos triggers que se disparan en el proceso del borrado.

SYS.AW_DROP_TRG
SYS.XDB_PI_TRIG

·       La siguiente sentencia dinámica permite crear los scripts para el borrado de todos los sinónimos públicos. (Se recomienda crear un spool y ejecutarlos desde una consola de sqlplus con el usuario sys).

SQL>select 'drop public synonym "'||object_name||'";' from all_objects where object_type='SYNONYM' and OWNER='PUBLIC';

7.11  BLOQUEO EN LA BASE DE DATOS

·         Una de las principales gestiones sobre la base de datos en el momento de presentarse lentitud en las ejecuciones es la de verificar que no existan procesos bloqueados, lo anterior significa que sentencias sql que afectan los mismos objetos al mismo tiempo generan bloqueos entre si.
Para lo anterior se solicita efectuar la siguiente revisión.
-          Ejecutar la siguiente sentencia para validar los bloqueos entre usuarios.

SQL>select s1.username || '@' || s1.machine
  || ' ( SID=' || s1.sid || ' )  is blocking '
  || s2.username || '@' || s2.machine || ' ( SID=' || s2.sid || ' ) ' AS blocking_status
  from v$lock l1, v$session s1, v$lock l2, v$session s2
  where s1.sid=l1.sid and s2.sid=l2.sid
  and l1.BLOCK=1 and l2.request > 0
  and l1.id1 = l2.id1
  and l2.id2 = l2.id2 ;

De encontrar un bloqueo, determinar cuál es el SID del usuario que está bloqueando los demás y proceder a matar la sesión para liberar los demás.

7.12 TEST DE DIAGNOSTICO DE SU BASE DE DATOS

·        Una de las recomendaciones que de forma personal hago para los administradores de Oracle, es el de hacer un diagnóstico de cómo es el estado de la base de datos al momento de recibirla, este proceso le proporcionara una idea de cómo se encuentra la DB y cuáles serían las posibles mejoras a aplicar.
Anexo el siguiente cuadro como check-list que he elaborado para de forma secuencial detallar el estado de la DB con sus respectivos apuntes.

#
Detalle
SQL
Observaciones y soluciones
1
Tenga en claro las características del motor de la base de datos, su versión y la plataforma sobre la cual está montada.
Sql>select * from v$version;

Sql>select * from product_component_version;
Se detalla la información a nivel de versión del motor de Oracle y sobre que se encuentra instalado.
2
Consultar el nombre de la base de datos a revisar.
select value from v$system_parameter where name = 'db_name'
Determinar el nombre exacto de la base de datos a trabajar.
3
Consultar los parámetros de la Base de datos.
Sql>select * from v$parameter;

Sql>select * from v$system_parameter order by 2;
Se debe tener claro los valores de cada uno de los parámetros y de cuales se deben variar según los requerimientos o tamaño de la base de datos.
4
Consultar la ubicación de los archivos de DATA_FILES
Sql>select * from DBA_DATA_FILES;
El objetivo es poder definir la ruta de almacenamiento de los Datafiles para determinar su capacidad de almacenamiento físico y su espacio libre.
5
Consultar la ubicación de los archivos de control de la base de datos.
Sql>select value from v$system_parameter where name = 'control_files'
El objetivo es poder definir la ruta de almacenamiento de los Controlfiles para determinar su capacidad de almacenamiento físico y su espacio libre.
6
Consultar el tamaño de la base de datos.
SQL>SELECT SUM(BYTES)/1024/1024 MB FROM DBA_EXTENTS;
Poseer el dato del tamaño de la base de datos, sirve para calcular tiempos de ejecución según su tamaño, al igual que definir el tamaño de los espacios asignados a los backups de la base de datos.
7
Consultar los usuarios creados en la base de datos y su estado.
Sql>select username, account_status, created, profile from dba_users;
Define el número de usuarios creados en la base de datos y principalmente su status, lo cual permite determinar la depuración de los mismos.
8
Consultar de los anteriores usuarios existentes en la base de datos, cuáles de ellos son esquemas propietarios de objetos sin incluir el usuario SYS y SYSTEM.
SQL>select DISTINCT(owner)from all_objects where owner not in('SYS','SYSTEM');
Nos permite definir cuáles usuarios son propietarios de objetos, con el fin de analizar su respaldo y depuración.
9
Determine el valor asociado a la memoria de la base de datos
SQL>select sum(value) /1024/1024 from v$sga;
Compare el valor de la memoria asignada a la SGA con respecto a la memoria total del servidor de base de datos.
10
Determine el tamaño de los tablespace y la ubicación de los archivos que los componen
SELECT nvl(name,'=== TOTAL for DB ===') "Tablespace",TO_CHAR(SUM(mbytes),'999,999,999.99') "Total MB",TO_CHAR(SUM(used),'999,999,999.99') "Used MB",TO_CHAR(SUM(free),'999,999,999.99') "Free MB",ROUND(AVG(pct_used),2) "%Used",ROUND(AVG(pct_free),2) "%Free",'SQLDEV:GAUGE:0:100:0:0:'||AVG(pct_used) AS "Tablespace Utilization",TO_CHAR(SUM(new_sz),'999,999,999.99') "Expected Size",TO_CHAR(SUM(add_sz),'999,999,999.99') "Add MB for desired %" FROM(SELECT name, ROUND(mbytes,2) mbytes, ROUND(used,2) used, ROUND(free,2) free, ROUND(pct_used,2) pct_used, ROUND(pct_free,2) pct_free, ROUND(CASE WHEN ( ((100 *used)/ trim(:desired_pct))-mbytes < 0)THEN mbytes ELSE ((100*used)/ trim(:desired_pct)) END,2) new_sz, ROUND(CASE WHEN ( (((100 *used)/ trim(:desired_pct))-mbytes) < 0 )THEN 0 ELSE (((100 *used)/ trim(:desired_pct))-mbytes) END,2) add_sz FROM (SELECT NVL(b.tablespace_name, NVL(a.tablespace_name,'UNKOWN')) name, mbytes_alloc Mbytes, mbytes_alloc-NVL(mbytes_free,0) used, NVL(mbytes_free,0) free,((mbytes_alloc-NVL(mbytes_free,0))/ mbytes_alloc)*100 pct_used,100-(((mbytes_alloc-NVL(mbytes_free,0))/ mbytes_alloc)*100) pct_free FROM(SELECT SUM(bytes)/1024/1024 Mbytes_free,MAX(bytes)/1024/1024 largest,tablespace_name FROM dba_free_space GROUP BY tablespace_name) a,(SELECT SUM(bytes)/1024/1024 Mbytes_alloc, SUM(maxbytes)/1024/1024 Mbytes_max,tablespace_name FROM sys.dba_data_files GROUP BY tablespace_name
UNION ALL
SELECT SUM(bytes)/1024/1024 Mbytes_alloc, SUM(maxbytes)/1024/1024 Mbytes_max, tablespace_name FROM dba_temp_files GROUP BY tablespace_name) b WHERE a.tablespace_name (+) = b.tablespace_name))
GROUP BY rollup (name)ORDER BY CASE WHEN name IS NULL THEN 200 ELSE ROUND(AVG(PCT_FREE),2) END ASC;
Esta consulta nos permite determinar la ubicación de los archivos asociados a los tablespace, su tamaño, su consumo y posible depuración.
11
Usuarios con tablespace SYSTEM como temporary tablespace (exceptuando users SYS y SYSTEM)
SQL>select username,default_tablespace from dba_users where default_tablespace = 'SYSTEM'  and username not in ('SYS','SYSTEM')
Reubicar los usuarios según el informe y según los parámetros de configuración.
12
Usuarios con tablespace SYSTEM como default tablespace (exceptuando users SYS y SYSTEM)
SQL>select username,temporary_tablespace from dba_users where temporary_tablespace = 'SYSTEM' and username not in ('SYS','SYSTEM');
Reubicar los usuarios según el informe y según los parámetros de configuración.
13
Índices UNUSABLES
SQL>select owner,count(1) from dba_indexes where status = 'INVALID' group by owner

SQL> select owner,  index_name,     index_type, table_owner, table_name,      table_type, tablespace_name from dba_indexes
where status = 'INVALID'
Identifique según el listado generado y reconstruya los índices necesarios.
14
Objetos inválidos
SQL>select owner,object_type,count(1) from dba_objects where status = 'INVALID' group by owner,object_type order by 1,2
SQL> select owner, object_name,     created, last_ddl_time from dba_objects where status = 'INVALID' order by 1,2
Valide los objetos inválidos y compile nuevamente o elimine los que no son usados según análisis y autorización del propietario de los objetos. (ver 7.3 OBJETOS INVALIDOS)
15
Paquetes con bodies sin que no tengan sus correspondientes headers
SQL>select unique owner,name from dba_source a where type = 'PACKAGE BODY' and not exists (select null              from dba_source b where a.owner = b.owner and a.name = b.name                   and b.type = 'PACKAGE')
Identificar el bodie y validar su contenido y asociar su headers o recompilar, en caso contrario eliminar dicho package.
16
Constraints deshabilitadas
SQL>select owner, case constraint_type          when 'P' then 'PRIMARY_KEY'          when 'R' then 'FOREIGN_KEY'          when 'U' then 'UNIQUE' when 'C' then 'CHECK' end constraint_type, count(1) from dba_constraints where status = 'DISABLED' group by owner,constraint_type order by 1,2
Identificar y validar los constraints deshabilitados y depurar según la necesidad.(habilitar o eliminar)
17
Identificar bloqueos en la base de datos.
SQL>select s1.username || '@' || s1.machine  || ' ( SID=' || s1.sid || ' )  is blocking '  || s2.username || '@' || s2.machine || ' ( SID=' || s2.sid || ' ) ' AS blocking_status   from v$lock l1, v$session s1, v$lock l2, v$session s2  where s1.sid=l1.sid and s2.sid=l2.sid and l1.BLOCK=1 and l2.request > 0 and 1.id1 = l2.id1 and l2.id2 = l2.id2 ;
Identificar proceso o SID del usuario que está bloqueando las demás sesiones. Matar la sesión para liberar las demás.
18
Controlar que la tabla de auditoria sys.aud$ no esté en el tablespace SYSTEM
SQL>select name, value, display_value,      description from v$parameter where name like 'audit%'

SQL>select owner, segment_name,      tablespace_name from dba_segments
where segment_name = 'AUD$'
Identificar una de las tablas de mayor consume no este sobre el tablespace propio del sistema, se recomienda crear un tablespace solo para la auditoria.
19
Jobs en estado broken
SQL>select * from dba_jobs where sysdate > next_date
Identificar las tareas (jobs) existentes en la base de datos que no están funcionales para activarlos o en caso contrario depurar.
20
Jobs con fallas
SQL>select * from dba_jobs where failures > 0
Identificar y depurar según necesidad.
21
Roles no otorgados a ningún rol o user
SQL>select role from dba_roles
minus
select granted_role from dba_role_privs
Identificar y depurar según necesidad.
22
Sinónimos públicos que apuntan a objetos inexistentes
SQL>select * from dba_synonyms a
where owner = 'PUBLIC'
  and not exists (select null
                  from dba_objects b
                  where a.table_owner = b.owner
                    and a.table_name  = b.object_name)
Identificar y depurar según necesidad.
23
Sinónimos privados que apuntan a objetos inexistentes
SQL>select * from dba_synonyms a
where owner != 'PUBLIC'
  and not exists (select null
                  from dba_objects b
                  where a.table_owner = b.owner
                    and a.table_name  = b.object_name)
Identificar y depurar según necesidad.
24
Database links que son inaccesibles
begin
    for i in (select decode(owner,'PUBLIC',user,owner) owner,db_link from dba_db_links) 
loop
       begin
           execute immediate 'create view 'i.owner'.TEST as select count(1) c from dual@'i.db_link;
           dbms_output.put_line('OWNER: 'i.owner'; DB_LINK: 'i.db_link' --> ACCESIBLE');
           execute immediate 'drop view 'i.owner'.TEST';
           exception when others then                 dbms_output.put_line('OWNER: 'i.owner'; DB_LINK: 'i.db_link' --> NO ACCESIBLE');
end;
    end loop;
end;
/
Identificar y depurar según necesidad, quitar la asociación a los dblinks falsos ya que puede presentar objetos inválidos.
25
Segmentos con más de 100 extents (excluir sys y system)
select * from dba_segments where extents > 100 and owner not in ('SYS','SYSTEM')
Identificar y depurar según necesidad.
Analizar para posible particionamiento o redistribución.
26
Tablas no analizadas con más de 1000 registros
SQL>set serverout on size 500000
DECLARE l_cnt int;
BEGIN
FOR i IN (SELECT * FROM dba_tables
WHERE last_analyzed IS NULL
AND owner NOT IN ('SYS','SYSTEM'))
LOOP
EXECUTE IMMEDIATE ' select count(1) from '||i.owner||'.'||i.table_name
INTO l_cnt;
IF (l_cnt > 1000) THEN            DBMS_OUTPUT.put_line(i.owner||'.'||i.table_name);
END IF;
END LOOP;
END;
Identificar y depurar según necesidad.
27
Tablas con más de 5 índices
SQL>select owner,table_name,count(1) as no_indx from dba_indexes where owner not in ('SYS','SYSTEM') group by owner,table_name having count(1) > 5;
Identificar y analizar el paso a seguir respecto a la cantidad de indices asociados a una table. Tenga en cuenta que no existe limite pero por cada índice que tenga un tabla, Oracle deberá actualizarlo cada vez que hagas una transacción, update, insert, incluso para consultas de datos revisara los índices existentes a ver si puede utilizar alguno para mejorar el tiempo de respuesta, esto quiere decir a mas índices más lentitud.
28
Tablas que no posean primary key y que no estén vacías
SQL>select owner,table_name from dba_tables a where owner not in ('SYS','SYSTEM')  and num_rows > 0 and not exists (select null from dba_constraints b                  where a.owner = b.owner and a.table_name = b.table_name                     and b.constraint_type = 'P') order by 1,2
 Según el modelo E-R, toda entidad debe tener una clave primaria. Esto no es opcional, porque necesariamente necesitas algo que te permita no sólo identificar un único registro dentro de una tabla que puede tener millones, sino que también necesitas un modo de asegurarte que el registro se refiera a una instancia que no esté repetida. Y para eso es la PK.
29
Indices no analizados
select owner,index_name,table_name from dba_indexes where owner not in ('SYS','SYSTEM')and last_analyzed is null
Identificar y depurar según necesidad.
Analizar para posible particionamiento o redistribución.
30
Tablas con más de 100 columnas
SQL>select owner,table_name,count(1) from dba_tab_columns where owner not in ('SYS','SYSTEM') group by owner,table_name
having count(1) > 100
Identificar las tablas que por su tamaño en campos nos pueden presentar lentitud.
31
Owners que comparten tablespaces
select a.owner,b.owner, a.tablespace_name, a.cantseg,     b.cantseg from (select owner,tablespace_name,count(1) cantseg from dba_segments where owner not in ('SYS','SYSTEM','SYSMAN','OUTLN','DBSNMP','SYSAUX') group by owner,tablespace_name) a,  (select owner,tablespace_name,count(1) cantseg from dba_segments where owner not in ('SYS','SYSTEM','SYSMAN','OUTLN','DBSNMP','SYSAUX') group by owner,tablespace_name) b where a.tablespace_name = b.tablespace_name  and a.owner != b.owner order by a.cantseg+b.cantseg desc
Analizar y de ser necesario depurar según standares de aplicación o funcional.
32
Tablas Candidatas para Particionar
select unique a.object_owner,a.object_name from dba_hist_sql_plan a, dba_tab_statistics b where a.object_owner = b.owner and a.object_name = b.table_name and a.options = 'FULL' and b.num_rows > 10000000
Analizar y depurar.

7.13           TRUCOS SQL

7.13.1 Count(*)  

Cuando haces una consulta con un count(*) solo para ver cuantos registros tiene una consulta, no deberías utilizar el * ya que la base de datos va y busca las columnas definidas de la tabla(y esto por pequeño que sea toma tiempo) en estos casos puedes hacer algo así:

select count(1) from usuario ;
//el 1 representa el primer campo de la tabla
ó
select count(campo) from usuario ;

En conclusión el tiempo que se toma en ejecutar el segundo query es menor  al que se toma si el query tiene el count(*).

7.13.2 Truncate o Delete:

·       Cuando vas a borrar todos los registro de una tabla en la base de datos generalmente
hacemos

SQL>delete from usuario;

Cuando hacemos un delete la base de datos, borra uno a uno los registros de la tabla, los almacena por así decirlo en cache por si de pronto vamos a hacer rollback, esto ocupa más tiempo y espacio en la db sin mencionar que la mantiene bloqueada durante el proceso.(bloqueada quiere decir que si otros usuario la están consultando la transacción queda en cola y no es atendida hasta que la transacción que está haciendo el borrado la libera).

El truco es utilizar truncate porque es más rápido básicamente consiste en mover el índice del registro en el cual estaba el último registro de la bd al primer registro y no borramos uno a uno, sino que como el registro queda en el inicio entonces es como si no hubiera nada.

Como utilizarlo, sencillo:

SQL>truncate usuario;//donde usuario es la tabla

En conclusión, si estás seguro de que vas a borrar todos los datos y no te vas a arrepentir es mejor truncate.

7.13.3 Drop – purge

·        Cuando borramos una tabla de la base de datos oracle, dependiendo de la forma en la que borremos la tabla va a ser eliminada completamente o solo aparentemente borrada, veamos el siguiente código.

SQL>drop table usuario;

La tabla se borra pero realmente queda almacena como una papelera, la prueba está en que el tamaño del tablespace no disminuye. La alternativa para que realmente quede eliminada la tabla y no nos ocupe espacio es agregar al final la palabra purge y quedaría así.

SQL>DROP TABLE USUARIO PURGE;

7.13.4 indices

Indexar nuestras columnas es una de las maneras más comunes y sencillas de optimizar nuestras consultas. Sin embargo, se debe tener un profundo conocimiento sobre cómo funciona el indexado en cada DMBS para utilizar correctamente sus índices. Dicho de otro modo: crear índices sin sentido, sencillos o absurdos sin comprender exactamente cómo funciona nuestra Base de Datos puede tener justamente el efecto contrario al deseado en nuestras consultas, y hacer que funcionen aún más lentas.


7.13.5 SÍMBOLOS OPERACIONALES

Los símbolos operacionales como >,<,=,!=, etc. son muy útiles en nuestras consultas. Se pueden optimizar algunas consultas si la columna con el símbolo operacional en cuestión esta indexada. Por ejemplo:

SQL>SELECT * FROM MI_TABLA WHERE COLUMNA > 16;

Esta consulta no está optimizada, ya que el motor de la Base de Datos debe buscar el valor 16, y DESPUÉS escanear hacia delante del 16 y por detrás.

       SQL>SELECT * FROM MI_TABLA WHERE COLUMNA >= 17;

De este modo, el DMBS debe saltar directamente a los valores mayores a 16. Es casi la misma manera, pero evitas que el DMBS tenga que comparar también los valores menores para ver si entran en la query.

7.13.6 COMODINES

En SQL, el comodín se nos presenta con el símbolo ‘%’. Usar comodines ralentiza bastante nuestras consultas, especialmente si la tabla en la que buscamos en bastante grande. Se pueden optimizar dichas consultas si podemos permitirnos poner el comodín únicamente como comodín-sufijo, en vez de como comodín-prefijo o como comodín-total.

#Comodín-total:
SQL>SELECT * FROM MI_TABLA WHERE COLUMNA LIKE '%manzana%';

#Comodín-sufijo:
SQL>SELECT * FROM MI_TABLA WHERE COLUMNA LIKE 'manzana%';

#Comodín-prefijo:
SQL>SELECT * FROM MI_TABLA WHERE COLUMNA LIKE '%manzana';


7.13.7 OPERADOR NOT

Intenta evitar todo lo posible el operador NOT en SQL. Es mucho más rápido buscar por un valor exacto (operador positivo) como por ejemplo un LIKE, IN, EXISTS o el símbolo operacional =, en vez de usar un operador negativo como NOT LIKE, NOT IN, NOT EXIST o el símbolo != . Usar un operador negativo provoca que la búsqueda tenga que recorrer cada línea por separado identificar los que no son o que existen dentro de la tabla. Sin embargo, usando un operador positivo, la búsqueda para en cuanto se encuentra el resultado.

7.13.8 COUNT Vs EXIST

Muchos de nosotros solemos usar el operador COUNT para determinar si existe un dato en particular.

SQL>SELECT COLUMNA FROM MI_TABLA WHERE COUNT(COLUMNA) > 0 ;

Este sistema es muy malo, ya que el COUNT debe contar cada registro de la tabla para ver cuantos hay. La mejor alternativa es usar el operador EXISTS, que para en cuanto encuentra el primer registro que concuerda con la búsqueda, sin tener que contarlos todos. Por lo tanto, existe.

7.13.9 COMODIN Vs SUBSTR

Si una columna en particular está indexada, es mejor usar un comodín para buscar en ella en vez de un Substr. El comodín usará el índice, el Substr no.
MAL:

SQL>SELECT * FROM MI_TABLA WHERE Substr(COLUMNA, 1, 1) = 'pepito';

Esta consulta hará Substr a cada registro de la tabla individualmente para rastrear el valor ‘pepito’. Pero del siguiente modo:

SQL>SELECT * FROM MI_TABLA WHERE COLUMNA = 'pepito%' ;

La consulta con comodín corre mucho más rápido en el DBMS. Ejemplo:
#BUSCAR TODAS LAS FILAS QUE EL PRIMER CARACTER ES 'E'

SQL>SELECT * FROM MI_TABLA WHERE COLUMNA = 'E%' ;

7.13.10 FUNCIONES MAX Y MIN

Las funciones Max y Min buscan el valor Máximo y Mínimo de una columna. Corrección: Podemos usar Max y Min en columnas que ya están indexadas. Pero si dicha columna se usa frecuentemente, tener un índice además debería ayudar a acelerar dichos operadores Max y Min. Decidir si poner un índice sólo para acelerar los Max y Min no es aconsejable. Los índices son caros de mantener, y crear índices sólo para acelerar Max y Min es una locura. Es como sacrificar el bosque entero por un único árbol.

7.13.11 TIPOS DE DATOS

Se deben usar los tipos de datos más eficientes (más pequeños) siempre que sea posible. Es innecesario, y algunas veces hasta peligroso, proporcionar tipos de datos enormes cuando se pueden solucionar un problema con tipos más pequeños. Por ejemplo, usando los tipos de datos enteros más pequeños para así tener tablas más pequeñas. MEDIUMINT es a veces una elección mejor que un INT, porque una columna MEDIUMINT uses 25% menos de espacio en disco. Por otro lado, VARCHAR es mejor que LONGTEXT para almacenar texto como un email, unas observaciones o pequeños detalles.

7.13.12 INDICES PRIMARIOS

La primera columna que se use para un indexado debe ser lo más corta posible. Esto hace que la identificación de cada fila sea más sencilla y eficiente por parte del DBMS. 

7.13.13 INDEXADO DE CADENAS

No es necesario indexar toda la cadena cuando en su lugar se pueden indexar un prefijo o sufijo de la cadena. Es aconsejable llevar a cabo dicha indexación especialmente si el prefijo o sufijo de la cadena proporciona un identificador único para la cadena.
Los índices más cortos son más rápidos, no sólo debido a que requieren menos espacio en disco, sino porque también evitan que se acceda a ellos menos veces y a la caché de índice, y por lo tanto menos búsquedas en disco.

7.13.14 LIMITAR EL RESULTADO

Otro método muy común para optimizar tu query es minimizar el número de filas devueltas. Si en una tabla tienes varios billones de registros y lanzas una simple pero potente query sin limitación, puedes echar abajo la Base de Datos entera.

SQL>SELECT * FROM MI_TABLA;

Si sólo necesitas una cantidad de registros, trata de limitar el resultado. De esta forma no sólo serás más eficaz, sino que ayudarás a minimizar el daño que puede ocasionar un ataque por SQL de este tipo.

SQL>SELECT * FROM MI_TABLA WHERE 1 LIMIT 10 (Sólo en MySQL) ;

7.13.15 USAR EL VALOR POR DEFECTO

Debemos aprovechar la ventaja que tienen esta Bases de Datos por el hecho de tener columnas con valores por defecto. Inserta valor explícitamente únicamente cuando sean diferentes de los valores por defecto. Esto reduce el análisis (parsing) que Oracle debe hacer y mejora la velocidad de los INSERTS.

7.13.16 SUBQUERY EN UN IN

Muchos de nosotros usamos una sub-consulta (o subquery) dentro de un operador IN, tal que así:

SQL>SELECT * FROM MI_TABLA WHERE COLUMNA IN ( SELECT COLUMNA2 FROM MI_TABLA2 );

Hacer esto es muy caro para el DBMS porque la consulta SQL debe evaluar la query exterior antes que la interior. En vez de esto, podemos usar lo siguiente:

SQL>SELECT * FROM MI_TABLA, (SELECT COLUMNA2 FROM MI_TABLA2) as dummytableWHERE dummytable.COLUMNA2 = MI_TABLA.COLUMNA;

Usando una dummytable (o tabla tonta) es mejor que usar un operador IN para hacer una sub-consulta. Como alternativa, un operador EXIST también es mejor.

7.13.17 UNION EN VEZ DE OR

Los índices pierden su velocidad cuando se usan en situaciones de OR

SQL>SELECT * FROM MI_TABLA WHERE COLUMNA_1 = 'pepito' OR COLUMNA_2 = 'manolito';

Si hacemos la query anterior usando 2 consultas con UNION, estas sí usarán sus índices.

SQL>SELECT * FROM MI_TABLA WHERE COLUMNA_1 = 'pepito'
UNION
SELECT * FROM MI_TABLA WHERE COLUMNA_2 = 'manolito'

7.13.18 UNION Vs UNION ALL

Pongamos un ejemplo de una query de SQL en que queremos traer las filas de TABLA1 y TABLA2. Usamos UNION para unir las dos consultas. Hasta aquí todo correcto. Pero se puede optimizar si sabemos lo que vamos a obtener.

SQL>SELECT NOMBRE, APELL FROM TABLA_EMPLEADOS
UNION
SELECT NOM, APE FROM TABLA_CLIENTES;

Cuando se manda la sentencia al motor de la Base de Datos, realmente lo que hace es consultar TABLA1 con sus registros, consulta TABLA2 con sus registros, los junta, los ordena y les aplica un DISTINCT. Estas dos últimas tareas son las que ejercen más trabajo sobre el motor de Base de Datos, y las que más recursos gastan.

Si sabemos que los registros que vamos a obtener de TABLA1 y TABLA2 son completamente distintos, podemos usar UNION ALL, lo que evita que haga la ordenación y el DISTINCT en el motor de Base de Datos.
SQL>SELECT NOMBRE, APELL FROM TABLA_EMPLEADOS
UNION ALL
SELECT NOM, APE FROM TABLA_CLIENTES;

Al hacer el UNION ALL, realmente lo que se pide es "traeme todos los registros, aunque esten duplicados". Pero si sabemos que no van a devolverse registros duplicados, usando esta variante obtendremos el mismo resultado, pero de forma más eficiente.

7.13.19 BUSCAR EL CARÁCTER % EN UN LIKE DE UNA SELECT

Necesitábamos buscar en una tabla de la Base de Datos los textos que contuvieran el símbolo de tanto por ciento (%).
La siguiente consulta no generaría lo deseado.

SQL>SELECT * FROM MI_TABLA WHERE DESCRIPCION LIKE '%%%';

Usando la cláusula de ESCAPE puedes buscar caracteres que normalmente no los podrías buscar. Se pueden buscar cadenas de caracteres que incluyan uno o más caracteres comodín especiales. Para buscar el signo de porcentaje como un carácter, en lugar de buscarlo como un carácter comodín, hay que proporcionar la palabra clave ESCAPE y el carácter que queremos "ignorar". Si no pusiéramos ese símbolo de exclamación y el ESCAPE '!', devolvería cualquier fila.

SQL>SELECT * FROM MI_TABLA
 WHERE DESCRIPCION LIKE '%!%%' 
ESCAPE '!';

7.13.20 MUCHOS JOINS

Ojo con las consultas con muchas Joins, evitar cruzar muchas tablas. Por el mismo motivo se deben utilizar las vistas con mucho cuidado, puesto que provocan que se crucen muchas tablas inadvertidamente

7.13.21 CLONAR UNA TABLA

La siguiente sentencia nos permite hacer una copia de una tabla específica.

SQL> CREATE TABLE <nueva_tabla> AS SELECT * FROM <table_origen>;

Hay que tener en cuenta que con esta opción no recupera índices, estadísticas de tabla, ni nada por el estilo.

7.14 TRUNCATE vs DELETE

Adjunto un listado de las principales diferencias entre “truncar” y “deletear” todas las filas de una tabla.
-         TRUNCATE es una operación DDL y es rápido y DELETE una operación DML y es lento.
-         TRUNCATE resetea el HWM y libera el espacio, DELETE no.
-         TRUNCATE no tiene vuelta atrás, ni siquiera se puede hacer un flashback. Es raro que se pueda hacer flashback de un drop pero no de un truncate, no?. Con delete se puede hacer un rollback y si ya se confirmó el borrado (commit) se podría utilizar flashback.
-         TRUNCATE no dispara DML's triggers asociados a la tabla truncada.
-         TRUNCATE tiene un tratamiento especial para la MATERIALIZED VIEW LOG vinculada con la tabla.
-         DELETE puede utilizarse para eliminar un subconjunto de datos, con TRUNCATE hay que eliminar todas las filas. Sería bueno que existiera algo así como: TRUNCATE, WHERE .., no?
-         TRUNCATE no puede mantener foreign keys, por el contrario con DELETE podemos hacer delete cascade.
-         TRUNCATE inválida índices globales cuando se truncan particiones y subparticiones. Por suerte desde 9i R2 se pueden mantener los índices globales validos usando UPDATE GLOBAL INDEXES.
-         TRUNCATE puede validar índices que ya estaban inválidos y pasarlos a estado valido. Cuidado cuando para acelerar procesos de carga se deshabiliten los índices y luego se trunque, ya que la carga se hará con los índices habilitados.
Primero truncar y luego pasar los índices a inusables.
-         Ni TRUNCATE ni DELETE de todas las filas eliminan las estadísticas asociadas.
Sería interesante que se eliminen las estadísticas automáticamente con el TRUNCATE, no?.
-        TRUNCATE invalida los cursores que referencian a la tabla en cuestión.
            -     DELETE de tablas grandes genera una importante cantidad de UNDO y REDO.

2 comentarios:

  1. Exclente recopilacion de temas muchas gracias

    ResponderEliminar
  2. Great and I have a nifty offer you: Where Is Charlotte Church House Renovation home repairs contractors near me

    ResponderEliminar