¿Que se puede hacer para mejorar el rendimiento de las consultas en Oracle 11?


Tradicionalmente para mejorar el rendimiento de las consultas en SQL contra una BBDD Oracle , una vez optimizada la consulta con técnicas  de Tuneling ( por ejemplo usando Hints si procede) ,   siempre pasamos a dos técnicas clásicas :

  • Reconstruyendo los  índices existente en base de datos ,para lo cual previamente comprobaremos si esos índices deberían de ser reconstruidos  
  • Actualizando estadísticas sobre las tablas que realizamos las consultas.

Veamos mas despacio de que estamos hablando: 

 

Reconstrucción de indices

El índice de una base de datos es una estructura de datos que mejora la velocidad de las operaciones, permitiendo un rápido acceso a los registros de una tabla por lo que se suelen usar sobre aquellos campos sobre los cuales se vayan a realizar búsquedas frecuentes dado que su  funcionamiento es similar al índice de un libro: guardando duplas de elemento que se desea indexar junyo a su posición en la base de datos, de modo que para buscar un elemento que esté indexado, sólo necesitamos que buscar en el índice de dicho elemento para, una vez encontrado, devolver el registro que se encuentre en la posición marcada por el índice.

Los índices pueden ser creados usando una o más columnas, preparando la base de datos tanto para búsquedas rápidas al azar como para ordenaciones eficientes de los registros.

Los índices son construidos sobre árboles B, B+, B* o sobre una mezcla de ellos, funciones de cálculo u otros métodos.

El espacio en disco requerido para almacenar el índice es típicamente menor que el espacio de almacenamiento de la tabla (puesto que los índices generalmente contienen solamente los campos clave de acuerdo con los que la tabla será ordenada, y excluyen el resto de los detalles de la tabla), lo que da la posibilidad de almacenar en memoria los índices de tablas que no cabrían en ella. En una base de datos relacional un índice es una copia de parte de una tabla.

La siguiente consulta SQL mostrará el tamaño en megabytes de un índice determinado, en nuestro caso del índice PK_FACTURACION_CODIGO perteneciente a la tabla FACTURACION y el campo CODIGO del ejemplo. La consulta SQL para mostrar el tamaño ocupado por un índice es la siguiente:

select segment_name, sum(bytes)/1024/1024 MB
from dba_extents
where segment_name = ‘INDICE DE EJEMPLO’
group by segment_name

 

 

Es importante periódicamente examinar y determinar qué índices son susceptibles de ser reconstruidos. Cuando un índice está descompensado puede ser porque algunas partes de éste han sido accedidas con mayor frecuencia que otras dando como resultado problemas de contención de disco o cuellos de botella en el sistema.

Normalmente reconstruimos un índice con el comando ALTER INDEX  (esta sentencia se utiliza para cambiar o reconstruir un índice existente en la base de datos).

Para poder ejecutar este comando el índice debe de estar en el propio esquema donde intentes ejecutarlo o deberías de tener el privilegio alter any index. También tenemos que tener en cuenta que para realizar la reconstrucción de un índice deberíamos de tener cuota suficiente sobre el tablespace que lo lanzamos.

Para reconstruir un índice bastaría con lazar la siguiente sentencia

ALTER INDEX <index_name> REBUILD;

Para reconstruir una partición de un índice podríamos hacer lo siguiente

ALTER INDEX <index_name> REBUILD PARTITION <nb_partition> NOLOGGING;

Nota: En algunos casos cuando alguno de los índices tiene algún tipo de corrupción no es posible reconstruirlo. La solución en este caso es borrar el índice y recrearlo

 

 

 

Actualización de estadísticas

Cuando una base de datos Oracle recibe una sentencia “SQL” para resolver una consulta, se llevan a cabo diversas acciones para lograr la entrega del resultado.

Dentro de los diversos pasos uno de los más importantes es el llevado a cabo por el optimizador basado en costos “Cost Based Optimizer o CBO”. Para que el “CBO” pueda determinar de forma exacta el plan de ejecución de para un “SQL Query” debe disponer de la información de las estadísticas de las tablas e índices que participan en el “SQL Query”, esta información comúnmente es conocida como «Optimizer statistics» “Estadisticas del optimizador”, la misma describe como esta compuesto y distribuido internamente el objeto.

Estas estadísticas son utilizadas por el optimizador para elegir el mejor plan de ejecución para cada instrucción SQL.

El tiempo necesario para colectar las estadísticas en algunos casos puede ser de gran medida. En el manejador se pueden utilizar diversos métodos para tratar de reducir el tiempo de esta tarea en la mayor proporción posible.

 

Es importante tener por tanto actualizadas las estadísticas de la base de datos. Para saber si las estadísticas se están lanzando correctamente podemos hacer una consulta sobre la tabla ALL_INDEXES en oracle 11  (en Oracle 10  es  dba_indexes )y ver el campo last_analyzed para observar cuando se ejecutaron sobre ese índice las estadísticas.

Nota: la columna “LAST_ANALYZED” la cual puede ser encontrada en vistas tales como: “DBA_TABLES”, “DBA_INDEXES”, “DBA_TAB_COL_STATISTICS” indica la fecha en que fue calculada la estadística para dicho objeto por ultima vez.

 

Como ejemplo  , si queremos  saber cuando fue la ultima vez que se ejecutaron estadísticas sobre todas las tablas perteneciente a un determinado esquema de BBDD  lanzaremos la siguiente consulta:

SELECT LAST_ANALYZED,table_name FROM ALL_INDEXES ;

 

 

Como vemos con las fechas podemos  hacernos una idea , de lo actualizado que están las estadisticas   sobre cada tabla   

Para actualizar las estadísticas de forma global para  un  esquema de BBDD,  podemos utilizar  el paquete DBM_STATS  de la la siguiente forma:

Execute DBMS_STATS.gather_schema_stats(‘Esquema’);

 

Una vez actualizadas las estadísticas de los índices de la base de datos lanzamos la siguiente consulta:

SELECT LAST_ANALYZED,table_name FROM ALL_INDEXES ;
SELECT index_name, blevel,
DECODE(blevel,0,'OK BLEVEL',1,'OK BLEVEL',2,
'OK BLEVEL',3,'OK BLEVEL',4,'OK BLEVEL','BLEVEL HIGH') OK
FROM dba_indexes where table_owner='DBPROD08';

 

Con esta sentencia obtendremos el nombre nombre del índice, el blevel y si es correcto este indice.

Los índices que deberíamos de reconstruir son los que en la columna ok aparecen como BLEVEL HIGH.

Blevel (branch level) es parte del formato del B-tree del índice e indica el número de veces que ORACLE ha tenido que reducir la búsqueda en ese índice. Si este valor está por encima de 4 el índice debería de ser reconstruido.

.

Como acceder desde Java a contenidos de celdas en Excel


Lo cierto es puede que  en  ocasiones necesitemos procesar ficheros excel u otro tipo de hojas de cálculo de   forma automatizada, por ejemplo  para extraer  información  de determinadas celdas, manipular estas , generar ficheros de metadatos que  sigan ciertas reglas , etc

En este caso nos vamos a centrar en los documentos OLE 2 de Microsoft, que manipularemos  usando la libreria  Apache POI – the Java API for Microsoft Documents, que nos proporciona acceso a los diferentes tipos de ficheros de Microsoft que utilizan esta estructura como:  Excel Word o  Powerpoint, también hay otros proyectos dentro de esta API para Visio y Publisher por ejemplo

Como se  puede   imaginar de todos estos el más desarrollado es Excel Workbooks y es precisamente el tipo de fichero elegido para este ejemplo.

Antes de empezar  usaremos  la librería POI-HSSF and POI-XSSF – Java API To Access Microsoft, donde HSSF es el proyecto POI de implementación total en Java para ficheros excel.Esta libreria usa cuatro componentes fundamentalmente:

  • HSSFWorkbook: Representación de alto nivel de un libro (Workbook) que será nuestra documento excel. Es el primer objeto que construiremos si vamos a leer o escribir una hoja excel.
  • HSSFSheet: representación de alto nivel de una hoja excel, podemos elegir la hoja de la excel usando el HSSFWorkBook.
  • HSSFRow: representación de celda de una fila de la hoja excel, solo las filas que tienen filas se pueden añadir a la hoja.
  • HSSFCell: representación de una celda en una fila de la un hoja de la excel, la utilizaremos para manejar el contenido de la celda.

Bien, sin más  dilaciones,  empezamos  a  ver como podemos procesar ficheros de Tipo Excel  

Paso 1: Descargar librería Apache Poi Java API

Antes de nada, tenemos que descargar la librería API Apache Poi Java, para añadirla a nuestro proyecto, voy a explicar cómo hacerlo en el IDE que estoy usando para este ejemplo: Netbeans, en otros IDES será similar la forma de integración de las librerías.

En nuestro proyecto buscamos la carpeta Libraries nos colocamos encima y seleccionamos Add Library, no tiene mucho que explicar así que te lo mostraré en imágenes:

La librería es  https://www.apache.org/dyn/closer.lua/poi/release/bin/poi-bin-4.1.0-20190412.zip

 

 

 

 

Paso 2-Registro de la librería en nuestro proyecto

Una vez  descargada y descomprimida, nos iremos a Properties del proyecto , después a   Java Build Path y   pincharemos en la pestaña  Libraries

Aquí  pulsaremos Add External JARs  seleccionando «poi-4.01.jar» desde al ruta donde la hayamos dejado esta librería.

 

 

Paso 3-Incluir las librerías

Ahora toca pasarse  a  programar, pero antes , no debemos olvidar importar las  cuatro librerias  que comentaabamos al principio

  • HSSFRow: representación de celda de una fila de la hoja excel, solo las filas que tienen filas se pueden añadir a la hoja.
  • HSSFWorkbook: Representación de alto nivel de un libro (Workbook) que será nuestra documento excel. Es el primer objeto que construiremos si vamos a leer o escribir una hoja excel.
  • HSSFSheet: representación de alto nivel de una hoja excel, podemos elegir la hoja de la excel usando el HSSFWorkBook.
  • HSSFCell: representación de una celda en una fila de la un hoja de la excel, la utilizaremos para manejar el contenido de la celda

Visto asi , adjuntando otras librerias utiles , la cabecera de importaciones de librerias quedaria  asi: 

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;


import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;

import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFSheet;

import java.io.FileDescriptor;

import org.apache.poi.ss.usermodel.Cell;

 

Paso 4-Convertir al menos a  formato excel97  o superior

Antes de proseguir , en caso de tener ficheros excel en formato muy antiguos( por ejemplo Excel 95) .  deberemos  convertir estos   al menos al  formato excel 97,  para ello, en caso de que el formato sea de excel 95 deberemos abrir dicho fichero e irnos  a la opción   Guardar como seleccionamos   «libro de Excel 97-2003(*.xls)»

 

 

 

5-Pasemos al código

Una vez salvada  la hoja en formato excel 97 o superior  veamos un simple ejemplo de cómo capturar  los valores contenidos en las celdas

 

                      ——

      FileInputStream file = new FileInputStream(new File("C:\\prueba.xls"));

    // Crear el objeto que tendrá el libro de Excel

      XSSFWorkbook workbook = new XSSFWorkbook(file);

      /** Obtenemos la primera pestaña a la que se quiera procesar indicando el indice.

     * Una vez obtenida la hoja excel con las filas que se quieren leer obtenemos el iterator

      * que nos permite recorrer cada una de las filas que contiene.

      */



      XSSFSheet sheet = workbook.getSheetAt(0);

      Iterator<Row> rowIterator = sheet.iterator();

      Row row;

    // Recorremos todas las filas para mostrar el contenido de cada celda

      while (rowIterator.hasNext()){

          row = rowIterator.next();

         // Obtenemos el iterator que permite recorres todas las celdas de una fila

          Iterator<Cell> cellIterator = row.cellIterator();

          Cell celda;

          while (cellIterator.hasNext()){

            celda = cellIterator.next();

            // Dependiendo del formato de la celda el valor se debe mostrar como String, Fecha, boolean, entero...

            switch(celda.getCellType()) {

            case Cell.CELL_TYPE_NUMERIC:

                if( DateUtil.isCellDateFormatted(celda) ){

                   System.out.println(celda.getDateCellValue());

                }else{

                   System.out.println(celda.getNumericCellValue());

                }

                break;

            case Cell.CELL_TYPE_STRING:

                System.out.println(celda.getStringCellValue());

                break;

            case Cell.CELL_TYPE_BOOLEAN:

                System.out.println(celda.getBooleanCellValue());

                break;

            }

          }

      }


   // cerramos el libro excel

      workbook.close();


Como vemos en el ejemplo anterior , crearemos el objeto que tendrá el libro de Excel(FileInputStream file = new FileInputStream(new File(«xxx..) y obtenemos la primera pestaña a la que se quiera procesar indicando el indice 0 (  XSSFSheet sheet = workbook.getSheetAt(0);

Una vez obtenida la hoja excel con las filas que se quieren leer ,obtenemos el iterator que nos permite recorrer cada una de las filas que contiene. (  Iterator<Row> rowIterator = sheet.iterator()😉

Y  ya bastaría ir recorriendo todas las filas con un bucle  while ( while (cellIterator.hasNext()))  ,  lo cual nos  va  a permitir  procesar  el contenido de cada celda

Obtenemos el iterator(  row = rowIterator.next(); ) que permite recorrer todas las celdas de una fila y salvar el objeto en la variable row

Ahora ya dependiendo del formato de la celda de la variable   row  ,  el valor se debe mostrar como String, Fecha, boolean, entero

El programa tras recorrer todas las filas acaba cerrando el libro excel

 

 

Como ejemplo más elaborado , vamos a ver como procesar  una hoja excel s donde cada fila representa los diferentes metadatos , por lo que con el programa  iremos capturando dicha información diferente  celda a celda para cada fila , para ir componiendo una fila de texto con los diferentes metadatos  capturados   y  que separaremos por el carácter almohadilla ( #) .

Finalmente veamos  pues el ejemplo cuya finalidad es extraer de un excel un fichero csv de un modo automático via el siguiente programa en java;

 

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;


import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;

import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFSheet;

import java.io.FileDescriptor;

import org.apache.poi.ss.usermodel.Cell;

 

 

public class Main {

//private static String nombre;

public static void main(final String[] args) throws IOException {

String lineaslog=»»;
String lineaslog2=»\r\n»;

//fichero de log
File filelog = new File(«d://ficherolog.txt»);
FileOutputStream fos2log = new FileOutputStream(filelog);

 


//añadimos 4 ceros la contador y generamos nuevo nombre
nfichero= «salida.txt»;
System.out.println(«Nuevo nombre fichero salida:» + nfichero);
lineaslog=»Nuevo nombre fichero salida:» + nfichero;
fos2log.write(lineaslog.getBytes());
fos2log.write(lineaslog2.getBytes());
//Procesar el excel a txt


//Creamos nuevo fichero de metadatos del tipo texto
File file = new File(nfichero);
FileOutputStream fos1 = new FileOutputStream(file);
FileDescriptor fd = fos1.getFD();


FileOutputStream fos2 = new FileOutputStream(fd);

String rutaArchivo=files22[ i ] ; //directorio3;
System.out.println («Leyendo archivo :» + rutaArchivo );
lineaslog=»Leyendo archivo :» + rutaArchivo ;
fos2log.write(lineaslog.getBytes());
fos2log.write(lineaslog2.getBytes());

System.out.println («NOMBRE HOJA :»+ hoja);
lineaslog=»NOMBRE HOJA :»+ hoja;
fos2log.write(lineaslog.getBytes());
fos2log.write(lineaslog2.getBytes());



try (FileInputStream fileexcel = new FileInputStream(new File(rutaArchivo)))

{
System.out.println(«Leer archivo Excel «);
lineaslog=»Leer archivo Excel «;
fos2log.write(lineaslog.getBytes());
fos2log.write(lineaslog2.getBytes());

// leer archivo excel97
HSSFWorkbook worbook = new HSSFWorkbook(fileexcel);

System.out.println(«Leyendo la hoja del fichero excel»);
lineaslog=»Leyendo la hoja del fichero excel»;
fos2log.write(lineaslog.getBytes());
fos2log.write(lineaslog2.getBytes());

//obtener la hoja que se va leer
HSSFSheet sheet = worbook.getSheetAt(0);

System.out.println(«Obtener las hojas del fichero excel»);
lineaslog=»Obtener las hojas del fichero excel»;
fos2log.write(lineaslog.getBytes());
fos2log.write(lineaslog2.getBytes());
//obtener todas las filas de la hoja excel
Iterator<Row> rowIterator = sheet.iterator();

Row row;

System.out.println(«La ruta del fichero excel es rutaarchivo :»+rutaArchivo);
lineaslog=»La ruta del fichero excel es rutaarchiv:»+rutaArchivo;
fos2log.write(lineaslog.getBytes());
fos2log.write(lineaslog2.getBytes());


Integer nm3=0;

while (rowIterator.hasNext()) // se recorre cada fila hasta el final

{

//System.out.println(«linea1»);

row = rowIterator.next();
nm3=nm3+1;

//se obtiene las celdas por fila
Iterator<Cell> cellIterator = row.cellIterator();
Cell cell;

//se recorre cada celda
int indice=0;
while (cellIterator.hasNext()) {

// se obtiene la celda en específico y se la imprime
cell = cellIterator.next();

//System.out.print(«(i»+i +»)=»+ cell.getStringCellValue()+» | «);

switch (indice) //tomaremos diferentes columnas del fichero excel
{

case 0:
{
nombre_fichero=cell.getStringCellValue();
}
case 1:
{
boletin=cell.getStringCellValue();
}
case 2:
{
telefono=cell.getStringCellValue();
}

case 3: // cif o nif
{
ncif=cell.getStringCellValue();
}

case 4: //valor del cif
{
cif=cell.getStringCellValue();
}

case 8:
{
fecha=cell.getStringCellValue();
}
case 9:
{
hora=cell.getStringCellValue();
}
}



linea= linea + cell.getStringCellValue() +»#»;

indice=indice+1;

}


lineas=boletin +»#» +telefono +»#»+ncif+»#» + cif+»#»+tipo +»#» +fecha+» «+hora+»#» +»#» +»#» +»#»+ nombre_fichero+ «#»+ nombre_fichero+»#»;


fos2.write(lineas.getBytes());

//System.out.println();

lineas=»\r\n»;

fos2.write(lineas.getBytes());

}

worbook.close();


linearesumen= directorio+»;»+ hoja +»;»+directorio22+ «P5_»+ fechaok+String.format(«%05d», contador) +»;»+nm3;

fos3.write(linearesumen.getBytes());

linearesumen=»\r\n»;

fos3.write(linearesumen.getBytes());

nm3=0;

}
catch (Exception e)

{

e.getMessage();
System.out.println(e.getMessage());
lineaslog=e.getMessage();
fos2log.write(lineaslog.getBytes());
fos2log.write(lineaslog2.getBytes());

}

fos1.close();
fos2.close();

System.out.println(«Fin de generacion del txt de metadatos desde excel a txt»);
lineaslog=»Fin de generacion del txt de metadatos desde excel a txt»;
fos2log.write(lineaslog.getBytes());
fos2log.write(lineaslog2.getBytes());

}