domingo, 29 de enero de 2017

Leyendo archivos con UTL_FILE en ORACLE


Objetivos:
• Leer archivos de texto con PL/SQL.
• Procedimiento GET_LINE del Paquete UTL_FILE.
• Ver ejemplos de uso práctico.

____________________________________________________________________________________
Procedimiento GET_LINE.
Ver Manejando Archivos de Texto con PL/SQL (UTL_FILE): Para mas información y ejemplos del Paquete UTL_FILE.

El Procedimiento GET_LINE lee texto del archivo tipo UTL_FILE.FILE_TYPE previamente abierto y coloca el texto en el parámetro de salida búfer. El texto es leído hasta el terminador de línea(Sin incluir dicho terminador) o hasta el final del archivo o hasta el final del parámetro len. No es posible exceder max_linesize especificado en FOPEN.

Nota: Si la línea no cabe en el búfer, se genera la excepción VALUE_ERROR. Al llegar al final del archivo y no encontrar texto alguno, se genera la excepción NO_DATA_FOUND. Si se abre el archivo para operaciones en modo byte (RB, WB, AB) o modo escritura (W), se genera la excepción INVALID_OPERATION.

Sintaxis:
UTL_FILE.GET_LINE
(
  file        IN  FILE_TYPE,
  buffer      OUT VARCHAR2,
  len         IN  PLS_INTEGER DEFAULT NULL
);
Donde:
Parámetro
Descripción
file
Identificador de archivo activo retornado por la llamada de FOPEN.

El archivo debe estar abierto en modo lectura (R); De lo contrario se genera la excepción INVALID_OPERATION.
buffer
Búfer de datos para recibir la línea leída del archivo
len
El número de bytes leídos del archivo. El valor predeterminado es NULL. Si es NULL, Oracle proporciona el valor de max_linesize.
____________________________________________________________________________________
Ejemplos:
CREATE OR REPLACE DIRECTORY dir_read
  AS  'F:\Directory_Read';
/*Primero creamos un directorio de Base de Datos con la ruta donde residirá el Archivo a ser leído. Nota: El directorio físico (La Carpeta) debe ser creado manualmente.*/
---
/*La anterior imagen muestra el directorio físico creado manualmente. También notar el Archivo: Source_File.txt a ser leído con el procedimiento GET_LINE.*/
---
/*La anterior imagen muestra el contenido del archivo: Source_File.txt. Click Aquí para Descargar.*/
---
SET SERVEROUTPUT ON
DECLARE
    TYPE    typ_rec IS RECORD
    (
        line    NUMBER,
        column  VARCHAR2(14)
    );

    TYPE    typ_field
                IS TABLE OF typ_rec;

    v_field     typ_field;
    v_last      NUMBER  :=  0;
    v_file      UTL_FILE.FILE_TYPE;
    v_line      VARCHAR2(500);
 BEGIN

    v_file      := UTL_FILE.FOPEN
                                 (
                                    'DIR_READ',      --Nombre del Directorio en Mayúsculas
                                    'Source_File.txt',
                                    'R'
                                 );
    LOOP
        BEGIN
            v_field     :=  typ_field();

            UTL_FILE.GET_LINE(v_file, v_line);
            FOR i IN 1..6 LOOP
                v_field.EXTEND;
                v_field(i).line  :=  INSTR(v_line,'|',v_last+1,1);
                
                CASE
                    WHEN    i = 1 THEN
                        v_field(i).column   :=  'LINE';
                    WHEN    i = THEN
                        v_field(i).column   :=  'NAME';
                    WHEN    i = THEN
                        v_field(i).column   :=  'NATIONALITY';
                    WHEN    i = THEN
                        v_field(i).column   :=  'AGE';
                    WHEN    i = THEN
                        v_field(i).column   :=  'MARITAL_STATUS';
                    ELSE
                        v_field(i).column   :=  'CHILDREN_COUNT';
                END CASE;

                v_last  :=  v_field(i).line;

            END LOOP;
            v_last  :=  0;

            DBMS_OUTPUT.PUT_LINE(RPAD('=',LENGTH(v_line),'='));
            DBMS_OUTPUT.PUT_LINE(v_line);
            DBMS_OUTPUT.PUT_LINE(RPAD('=',LENGTH(v_line),'='));

            DBMS_OUTPUT.PUT_LINE(v_field(1).column||': '||SUBSTR(v_line,1,v_field(1).line-1));
            FOR i IN 1..5 LOOP
                DBMS_OUTPUT.PUT_LINE(v_field(i+1).column||': '||
                                        SUBSTR
                                            (
                                                v_line,v_field(i).line+1,v_field(i+1).line-v_field(i).line-1
                                            )
                                    );
            END LOOP;
            DBMS_OUTPUT.PUT_LINE('VEHICLE: '||SUBSTR(v_line,v_field(6).line+1,LENGTH(v_line)));

            EXCEPTION
                WHEN NO_DATA_FOUND THEN
                    EXIT;
        END;
    END LOOP;

    UTL_FILE.FCLOSE(v_file);

 END;
 /*Sin dar muchos detalles, en este ejemplo vemos como usamos el procedimiento GET_LINE para recorrer linea por linea el archivo en cuestión y asignar a la variable de salida el valor contenido. Notar que el mismo procedimiento lee una linea a la vez y avanza a la siguiente, una vez que no encuentra mas lineas se produce la EXCEPTION: NO_DATA_FOUND.*/
---OUTPUT:
CREATE TABLE hr.people_details
(
    line                NUMBER,
    name                VARCHAR2(50),
    nationality         VARCHAR2(50),
    age                 NUMBER(2),
    marital_status      VARCHAR2(15),
    children_count      VARCHAR2(9),
    vehicle             VARCHAR2(50)
);
/*Creamos la tabla: people_details donde insertaremos los datos extraídos del Archivo: Source_File.txt.*/
---
DECLARE
    TYPE    typ_rec IS RECORD
    (
        line    NUMBER,
        v_people    hr.people_details%ROWTYPE
    );

    TYPE    typ_field
                IS TABLE OF typ_rec;

    v_field     typ_field;
    v_last      NUMBER  :=  0;
    v_file      UTL_FILE.FILE_TYPE;
    v_line      VARCHAR2(500);
 BEGIN

    v_file      := UTL_FILE.FOPEN
                                 (
                                    'DIR_READ',      --Nombre del Directorio en Mayúsculas
                                    'Source_File.txt',
                                    'R'
                                 );
    LOOP
        BEGIN
            v_field     :=  typ_field();

            UTL_FILE.GET_LINE(v_file, v_line);
            FOR i IN 1..6 LOOP
                v_field.EXTEND;
                v_field(i).line  :=  INSTR(v_line,'|',v_last+1,1);

                v_last  :=  v_field(i).line;

            END LOOP;
            v_last  :=  0;

            v_field(1).v_people.line    := SUBSTR(v_line,1,v_field(1).line-1);
            v_field(1).v_people.name    := SUBSTR
                                            (
                                                v_line,v_field(1).line+1,v_field(2).line-v_field(1).line-1
                                            );
            v_field(1).v_people.nationality := SUBSTR
                                            (
                                                v_line,v_field(2).line+1,v_field(3).line-v_field(2).line-1
                                            );
             v_field(1).v_people.age    := SUBSTR
                                            (
                                                v_line,v_field(3).line+1,v_field(4).line-v_field(3).line-1
                                            );
             v_field(1).v_people.marital_status
                                        := SUBSTR
                                            (
                                                v_line,v_field(4).line+1,v_field(5).line-v_field(4).line-1
                                            );
             v_field(1).v_people.children_count
                                        := SUBSTR
                                            (
                                                v_line,v_field(5).line+1,v_field(6).line-v_field(5).line-1
                                            );
            v_field(1).v_people.vehicle  :=  SUBSTR(v_line,v_field(6).line+1,LENGTH(v_line)-v_field(6).line);

            INSERT INTO hr.people_details
            VALUES  v_field(1).v_people;

            EXCEPTION
                WHEN NO_DATA_FOUND THEN
                    EXIT;
        END;
    END LOOP;

    UTL_FILE.FCLOSE(v_file);
 END;
/*Ahora modificamos el pasado ejemplo para que en lugar de mostrar los datos por pantalla, inserte dichos valores en la tabla: people_details.*/
---OUTPUT:

Nota: En los ejemplos antes presentados, aparte de implementar la funcionalidad del paquete UTL_FILE también uso otros objetos como TYPES y Estructuras de Control de la que no doy detalles, esto es debido a que dichos objetos han sido explicados en pasadas publicaciones del BLOG.

Aun así, si sienten algún tipo de duda o confusión pueden exponerlas con libertad vía comentario o mensaje privado.